DeleteView example with template

4,411 views
Skip to first unread message

Steve Kilbane

unread,
Apr 26, 2012, 4:41:44 PM4/26/12
to Django users
Hi all,

I've been banging my head against the docs, but I'm struggling with
the generic views. I get the concept of the views, but I'm finding it
difficult to work out what I should put in any given template. Am I
right in thinking that a GET of a DeleteView should generate a form
called "form"?

I'd find it helpful if each generic view also had an associated
generic template that rendered each of the view's context variables
(with their default names).

steve

Ejah

unread,
Apr 27, 2012, 4:17:28 AM4/27/12
to Django users
As the docs mention, yes, it does.
You provide a pk or slug via the url, identifying the object to be
deleted. You can add a form_class to specify the content of the
associated form, a model to tell django which model to use when
looking for the object to delete and a template_name to layout your
page.
In case of DeleteView your cont ext contains form.
DetailView contains object, unless you specify context_object_name.
UpdateView and CreateView also put form in the context.
You might want to take a look at godjango.com for some nice short
intro videos on generic class based views, and the source code.
Hth

Steve Kilbane

unread,
Apr 27, 2012, 7:59:38 AM4/27/12
to Django users
I'll take a look - many thanks, Ejah.

steve

Steve Kilbane

unread,
May 3, 2012, 3:22:15 PM5/3/12
to Django users


On Apr 27, 9:17 am, Ejah <ej.huijb...@gmail.com> wrote:
> You might want to take a look at godjango.com for some nice short
> intro videos on generic class based views, and the source code.


As it happens, the godjango videos didn't cover the templates, but
they did provide some clues - enough to get me going. So I'm posting
the kind of thing I ended up with here, in case it'll help someone
else struggling with the class-based generic views.

models.py:

This is a scheduling app, so people are assigned to scheduled items.
This model is used as the "through" model for that relation. You can
ignore the content of the model - it's not relevant to the use of
generic views, with the exception that I made sure to define
verbose_name and verbose_name_plural in everything. That's because I'm
using the same template to render things like lists of objects of
different models, and I want to have predictable names coming out.

class ItemPerson(models.Model):
item = models.ForeignKey(Item)
person = models.ForeignKey(Person)
role = models.ForeignKey(PersonRole,
default=PersonRole.objects.find_default)
status = models.ForeignKey(PersonStatus,
default=PersonStatus.objects.find_default)
visible = models.CharField(max_length=4, choices=YesNo,
default='Yes')
distEmail = models.CharField(max_length=4, choices=YesNo,
default='No')
recordingOkay = models.CharField(max_length=4, choices=YesNo,
default='No')

class Meta:
verbose_name = 'itemperson'
verbose_name_plural = 'itemspeople'

def __unicode__(self):
return u"%s: %s [%s]" % (self.item, self.person, self.role)
def get_absolute_url(self):
return mk_url(self)


forms.py:

I have a form for editing ItemPerson objects. This is used both when
creating and when editing the objects. The fromPerson field is some
plumbing I no longer need.

class ItemPersonForm(ModelForm):
fromPerson = forms.BooleanField(required=False,
widget=forms.HiddenInput)
class Meta:
model = ItemPerson


urls.py:

I have URLs to:
- list all the ItemPerson objects
- display the details of a given ItemPerson
- delete an ItemPerson
- edit an existing ItemPerson
- create a new ItemPerson
These all use subclassed generic views.

url(r'^progdb/itemspeople/$',
VisibleView.as_view(model=ItemPerson)),
url(r'^progdb/itemperson/(?P<pk>\d+)/$',
show_itemperson_detail.as_view()),
url(r'^progdb/delete_itemperson/(?P<pk>\d+)/$',
permission_required('progdb2.delete_itemperson')
(AfterDeleteView.as_view(
model=ItemPerson))),
url(r'^progdb/edit_itemperson/(?P<pk>\d+)/$',
permission_required('progdb2.change_itemperson')(EditView.as_view(
model = ItemPerson,
form_class=ItemPersonForm))),
url(r'^progdb/new_itemperson/$',
permission_required('progdb2.add_itemperson')(NewView.as_view(
template_name = 'progdb2/edit_itemperson.html',
model = ItemPerson,
form_class=ItemPersonForm))),


ListView
=======

I have two levels of subclass of ListView. The first level, AllView,
is because I'm using the same template to render lists of lots of
different models (not just this one), so I'm setting up both the
common template name and populating the context with names and labels
relating to the model.


class AllView(ListView):
template_name = 'progdb2/object_list.html'

def get_context_data(self, **kwargs):
context = super(AllView, self).get_context_data(**kwargs)
context['request'] = self.request
context['verbose_name'] = self.model._meta.verbose_name
context['verbose_name_plural'] =
self.model._meta.verbose_name_plural
context['new_url'] = r'/progdb/new_%s/' %
( self.model.__name__.lower() )
return context


The second level, VisibleView, is because I'm filtering the queryset
based on whether the user is logged in (I don't do this for all
models, hence two levels).


class VisibleView(AllView):
def get_queryset(self):
if self.request.user.is_authenticated():
return self.model.objects.all()
else:
return self.model.objects.filter(visible = True)


object_list.html:
I'm rendering the list with a common template that iterates over the
list, and has a link for creating a new object in the model. linky is
a filter that gives me a link to a particular object.

{% extends "progdb2/base.html" %}
{% load progdb_filters %}
{% block title %}{{ verbose_name_plural }} {% endblock %}
{% block body_content %}
<table border>
{% for o in object_list %}
<tr><td> {{ o|linky|safe }} </td></tr>
{% empty %}
<tr><td>None yet</td></tr>
{% endfor %}
</table>
{% if user.is_authenticated %}
<p><a href="{{ new_url }}">Add new {{ verbose_name }}</a>.</p>
{% endif %}
{% endblock %}




DetailView
========
I'm subclassing DetailView, in order to set a few attributes, and for
some of the models, I'm doing more. For ItemPerson, I could just be
setting all of these via keyword arguments to DetailView in urls.py.

class show_itemperson_detail(DetailView):
context_object_name = 'itemperson'
model = ItemPerson
template_name = 'progdb2/show_itemperson.html'



show_itemperson.html:
The template for the detail view shows the information for the
ItemPerson, and also gives a link to editing the object.

{% extends "progdb2/base.html" %}
{% load progdb_filters %}
{% block title %}{{ itemperson }} {% endblock %}
<p>
{% block body_content %}
<table border="1">
<tr>
<td>{{ object.person|linky|safe }}</td>
<td>{{ object.item|linky|safe }}</td>
<td>{{ object.role }}</td>
<td>{{ object.status }}</td>
</tr>
</table>
</p>
<p><a href="/progdb/edit_itemperson/{{ itemperson.id }}/">Edit
itemperson</a>.</p>
{% endblock %}




UpdateView
==========
UpdateView is subclassed because I'm using the same template
everywhere. This could just be a parameter to UpdateView in urls.py.
Other things like the model and the form class to use are passed as
parameters because they're different for each of my models.

class EditView(UpdateView):
template_name = 'progdb2/editform.html'


editform.html:

{% extends "progdb2/base.html" %}
{% load progdb_filters %
{% block title %}{{ form_title }} {% endblock %}
{% block body_content %}
{% if form_intro %}
<p>{{ form_intro }}</p>
{% endif %}
<form action="" method="post">
{% csrf_token %}
<table border>
{{ form.as_table }}
<tr><td colspan="2"><input type="submit" value="submit" /></td></tr>
</table>
</form>
{% endblock %}

DeleteView
=========
When I delete an ItemPerson, it'll most likely be because I was
following a deletion link from an Item or from a Person, so I want to
return back to that object after deletion. I use a form with two
submissions, and pass along the URLs for the items and the person in
question (because the object will be gone by the time I want them).

class AfterDeleteView(DeleteView):
def get_success_url(self):
if self.request.POST.has_key('after'):
return self.request.POST.get('after')
else:
return '/progdb/main/'

itemperson_confirm_delete.html:


{% extends "progdb2/base.html" %}
{% load progdb_filters %}
{% block title %}{{ form_title }} {% endblock %}
<p>
{% block body_content %}
<table border="1">
<tr>
<td>{{ object.person }}</td>
<td>{{ object.item }}</td>
<td>{{ object.role }}</td>
<td>{{ object.status }}</td>
</tr>
</table>
</p>
<form action="" method="post">
{% csrf_token %}
<input type="hidden"
name="after"
value="{{ object.item.get_absolute_url }}" />
<input type="submit"
value="Remove and return to {{ object.item }}"
name="DeleteAndShowItem" />
</form>
<form action="" method="post">
{% csrf_token %}
<input type="hidden"
name="after"
value="{{ object.person.get_absolute_url }}" />
<input type="submit"
value="Remove and return to {{ object.person }}"
name="DeleteAndShowPerson" />
</form>
{% endblock %}


CreateView
=========
Creating a new ItemPerson is similar, but more messy. I'll probably be
following a link from an Item (in which case I'd like to return to the
Item, and I don't know what Person will be added), or I'll be
following a link from a Person (and want to return to it), and won't
know what Item will be added. (this, btw, is where fromPerson came
from in ItemPersonForm; I should get rid of that now).

My solution was to have two submission buttons (again), but specify
the names of the model I want to return to, rather than the URL of the
object. Then I can determine the success URL from one side of the
relation or the other, after the new ItemPerson is created.

(If there are better ways of doing this, I'd like to know. :-> )

class NewView(CreateView):
template_name = 'progdb2/editform.html'

def get_initial(self):
models = { 'item': Item, 'person': Person }
initial = super(NewView, self).get_initial()
for k in self.request.GET:
if k in models:
id = int(self.request.GET[k])
m = models[k]
initial[k] = m.objects.get(id = id)
return initial

def get_success_url(self):
df = super(NewView, self).get_success_url()
subs = [ ('submit0', 'after0'), ('submit1', 'after1') ]
for (s, a) in subs:
if self.request.POST.has_key(s) and
self.request.POST.has_key(a):
attr = self.request.POST.get(a, 'self')
return getattr(self.object, attr).get_absolute_url()
return df

edit_itemperson.html:

{% extends "progdb2/base.html" %}
{% load progdb_filters %
{% block title %}{{ form_title }} {% endblock %}
{% block body_content %}
{% if form_intro %}
<p>{{ form_intro }}</p>
{% endif %}
<form action="" method="post">
{% csrf_token %}
<table border>
{{ form.as_table }}
<tr><td colspan="2">
<input type="submit"
name="submit0"
value="Save and show person" />
<input type="hidden"
name="after0"
value="person" />
</td></tr>
<tr><td colspan="2">
<input type="submit"
name="submit1"
value="Save and show item" />
<input type="hidden"
name="after1"
value="item" />
</td></tr>
</table>
</form>
{% endblock %}


steve



Reply all
Reply to author
Forward
0 new messages