Django Class-Based Generic Views and ModelForms - django

Like much documentation on generic views in Django, I can't find docs that explicitly describe how to use the new Class-Based Generic Views with Django Forms.
How is it done?

What have you tried so far? The class based views are pretty new, and the docs don't have a lot of examples, so I think you're going to need to get your hands dirty and experiment!
If you want to update an existing object, then try using UpdateView. Look at the mixins it uses (e.g ModelFormMixin, SingleObjectMixin, FormMixin) to see which methods you can/have to override.
Good luck!

The easiest way to use model forms with class based views is to pass in the model and keep a slug / pk captured in url, in which case you will not need to write any view code.
url(r'^myurl/$', CreateView.as_view(model=mymodel))
#Creates a model form for model mymodel
url(r'^myurl/(?<pk>\w+)/$', UpdateView.as_view(model=mymodel))
#Creates a model form for model mymodel and updates the object having pk as specified in url
url(r'^myurl/(?<slug>\w+)/$', DeleteView.as_view(model=mymodel, slug_field="myfield"))
#Creates a model form for model mymodel and deletes the object denoted by mymodel.objects.get(my_field=slug)
You can also override methods to obtain more complex logic. You can also pass a queryset instead of a model object.
Another way is to create a modelform in forms.py and then pass form_class to the url as
url(r'^myurl/$', CreateView.as_view(form_class=myform))
This method allows you to define form functions as well as Meta attributes for the form.

Related

Why can't I inject form into HTML?

I have a form that I want to inject into a class based DetailView.
forms.py
class PastLocationForm(forms.Form):
locations = forms.ModelChoiceField(queryset=Location.objects.all().order_by('location_name'))
views.py
class PatientDetailView(DetailView):
model=Patient
form_class = PastLocationForm
Unfortunately, the form PastLocationForm doesn't appear on the HTML page after injection. I inspected the page and there was nothing.
Interestingly, if I pass PastLocationForm to a functional view and render it for another page, the form shows up! I also have other views where I make use of "form_class" for other modelForms and they function correctly.
I will switch my view to functional view if I can't find the solution but I would rather keep the class based view.
The reason might be the fact that DetailView does not handle a form_class attribute (FormViews do), you'd need to use a mixin.
Check out this answer:
Django combine DetailView and FormView

Is there a way to add more than one model to a class based view?

Is there a way to add more than one model to a class based view ?!
This is my detail view.
class ArticleDetailView(DetailView):
model = Article
I want to add another model to my view .
I know i can use function based views in order to to have multiple models in my view.
But i'm wonder that is there any way i can do this with my class based view ?
Yes, but you have to handle excess models yourself. If you want to show details of two separate models in one view, you will need to override get_context_data. Of course you will need to write your custom methods to fetch the object.
If it is ambiguous to determine which model is main model (meaning that the majority of the view revolves around it and the other models are just supplementary, for example: a user profile needs user model and may need other models such as posts or favorite post etc., In this case the main model is user model), you should really use TemplateView to handle all of them yourself, instead of DetailView or ListView.
Check out this site, which has plenty of info to see what methods does class methods have and how to override them.

django update forms, who pass object id?

I've tried to create a simple ModelForm, and I notice that even if I pass an instance for update like that
mymodel = MyModel.objects.get(pk=1)
MyModelForm(instance=mymodel)
django does not create an hidden field or include in some way the pk of the object in the template. So I need to pass this by myself?
I prefer not passing the my id's like 1,2,3.. to the templates, so I would prefer passing something like uuid, or using signing.dumps(object_id), and then signing.loads(object_id), from django signing library.
So if I want to include this id in my template with the form POST data,
I didn't understand who is exactly responsible for the retrieve of that id - Is that the view or the form itself?
By view I mean to the built-ins FormView, or UpdateView, how these views find the object id? Assume to store the output of signing.dumps(object_id) in a hidden field
By the time you are in the template the form construction has completed. You can try accessing form.instance.id if its modelForm.
However, most likely you do not need the pk in the template, do you ? You can also inject a hidden form field with the instance pk value if you like. Why do you need the pk in the template ?
If you want to redirect to another page from the POST data you will have access to the object pk in the view itself.
According to official documentation the Built-in Views inherit from django.views.generic.detail.SingleObjectTemplateResponseMixin class which requires the views it is mixed with to provide a self.object attribute.

Django class based views success_url

I am trying to set the success_url on a django class based UpdateView but cannot get it to work. I have tried the syntax suggested in the docs
success_url="/polls/%(slug)s/"
But it is not working. How can I access the model fields in the success_url?
This relies on object field attributes. In the example you've posted, the model should have a slug field.
For related fields:
You can try using django's __ notation for related objects (e.g: user__username) in success_url, not sure if it'll work.
IMO in such cases a better practice is overriding get_success_url(), and returning the url taking into account self.object.

Model formsets and Date fields

I have a model formset on a model with a couple of date fields - which again is a DateTimeField in the model.
But when it displays in the template, it is shown as a text field.
How can I show it as a drop down box ?
WFormSet = inlineformset_factory(UserProfile, W, can_delete=False,exclude=[<list of fields to be excluded>], extra=3)
This is how I am intializing the modelformset.
How do I override these settings
The usual way to do this is to override the default field types in your ModelForm definition.
The example below would work if you had a DateField in your model (I note you have a DateTimeField... I'll come back to that in a sec). You're going to be specifying the exact same field type as would normally be specified, but you'll pass a different widget to the form field constructor.
from django.db import models
from django import forms
from django.forms.extras import SelectDateWidget
class MyModel(models.Model):
a_date_field = models.DateField()
class MyModelForm(forms.ModelForm):
a_date_field = forms.DateField(widget=SelectDateWidget())
class Meta:
model = MyModel
There isn't, to my knowledge, an equivalent widget provided for the DateTimeField in Django 1.0.x. In this case, you'll want to make a custom widget, perhaps subclassing SelectDateWidget. I note from a quick google on SelectDateTimeWidget that there have been several others who've been making what appear to be the widget you're seeking. I've not used them, so caveat emptor, but something like this SelectDateTimeWidget patch might be a good place to start.
Edit: When using a ModelFormset or InlineModelFormset, you can still achieve this by passing form=MyModelForm to the inlineformet_factory function:
MyModelFormset = inlineformset_factory(MyParentModel, MyModel, form=MyModelForm)
This works because the two model formset factories actually call the regular formset_factory constructor in their own implementations. It's a little hard to figure out because it's not explicitly stated in the docs... rather the Django docs allude to this ability by mentioning in passing that the model formset factories extend the normal formset_factory. Whenever I'm in doubt, I open django/forms/models.py to check out the full signature of the formset factory functions.