I have a form that allow users to log their activities. To make it simple, let's say I only have two fields that I want a user to fill out.
Time
Action
During a day, a user can fill out multiple time + action pairs. I used javascript on the front end to allow users to add these pairs as they wish.
Thus, I do not know how many pairs there will be beforehand. And thus, I cannot create a predefined ModelForm for it.
To deal with this issue, I labeled each Time and Action field with a unique name. So when I receive a POST request, I geta list like this inside the request.POST dictionary:
time_1: 9:50
action_1: wakeup
time_2: 11:00
aciton_2: workout
...
Then, I subtract each pair out of the dictionary and put them into a ModelForm for validation and save to the database.
class TimeActionModel(Model):
time = DateField()
action = CharField(max_length=100)
class TimeActionForm(ModelForm):
class Meta:
model = TimeActionModel
class TimeActionView(View):
def post(self, request, *args, **kwargs):
self._subtract_and_save(request)
def _subtract_and_save(request):
#loop through the request.POST dictionary
#pull out each pair
#stuff each one into a ModelForm object
if form.is_valid():
form.save()
Here is my quesiton:
Does this approach look right to you?
What's the 'Django way' of dealing with such situation?
Thank you!
There is a concept in Django called formset:
A formset is a layer of abstraction to work with multiple forms on the same page. It can be best compared to a data grid.
The Django way would be to use Model formsets:
Like regular formsets, Django provides a couple of enhanced formset classes that make it easy to work with Django models.
Therefore you could create a model formset for your TimeActionModel as such:
from django.forms.models import modelformset_factory
TimeActionFormset = modelformset_factory(TimeActionModel)
You can read more on that in the documentation. It has extensive use cases and examples to cover your case.
UPDATE: The extras parameter of the formset is not quite important. You can easily manipulate the number of extra forms in your formset with a bit of javascript. There are also contrib packages for that such as django-dynamic-formset.
UPDATE2: The name of the fields depends on the prefix used too, which I recommend it in case of many different forms/formsets in a single page, but you can easily deduce it looking at a default form that Django renders.
Also please take not not to forget in your template to include {{ my_formset.management_form }} and {{ my_formsets_form.id }}!
Related
I am beginning learning django and have just come across django forms (I've always used manual forms in the past in django templates).
Should I always use django forms no matter what or are there scenarios where I should write the forms in the templates manually?
You can use your own forms, but it is better to use django forms because it provides more customization and flexibility.
It is easy to change the type of "widget" rendered with very little code change when you use django forms.
For example:
class MyForm(forms.Form):
my_field = forms.ChoiceField(choices=CHOICES) #choices can be a tuple
Would render as a checkbox field.
If you want to render the same as a Radio button, all you would have to do is:
class MyForm(forms.Form):
my_field = forms.ChoiceField(widget=forms.RadioSelect, choices=CHOICES)
You would have to manually change the entire code if you have your own form.
You can also do some custom field level validation on the server side in one pass (Example, check if username is unique) - You can achieve the same in a custom form, but you will have to handle every scenario yourself.
Also, django has ModelForms which would be a replica of the Model object - that tremendously reduces the amount of work that needs to be done for validation, and form processing.
I'm trying to write a class-based view for Django which will utilise the same template each time, regardless of model. The intention is that I can then add a urls.py entry for each model, and not have to bother about a view or a template.
This will be used to display a form, and as the form field names are dependant on model type, the model needs to be examined and field names extracted at the view level, so they can be passed to the generic template. The template then generates the form based on field names and values of the object.
I've been really struggling with this. At the moment I'm working on overriding get_context_data as follows
def get_context_data(self, **kwargs):
context = kwargs
context_object_name = self.get_context_object_name(self.object)
if context_object_name:
context[context_object_name] = self.object
#add some custom stuff on too
tempdict = [(field, field.value_to_string(self)) for field in self.object._meta.fields]
#context.update({'datafields' : tempdict})
context.update({ 'blarg': 'tester!!'})
return context
The self.object._meta.fields bit is where I'm haivng the problems. I just can't get my head around how to access the current model. I'm doing this in a view, woud I have aany more luck in a mixin?
Thanks for your time.
O
I think you're going about this the wrong way. Django already knows how to create a form from a model, and forms know how to output themselves. So you can create a standard generic view using ModelFormMixin, there's no need to do anything clever to get form fields for a model's fields.
The only difficult bit is that you want one view to work for multiple models. So, rather than declaring the model explicitly on the view class, you'll need to work out some way of passing it dynamically - perhaps by overriding get_object.
If you're using django 1.3, class based views are included... Just use them and set the 'template_name' attribute to be your "common" name.
https://docs.djangoproject.com/en/1.3/topics/class-based-views/
I have a model named Domain which looks like this:
class Domain(models.Model):
"""
Model for storing the company domains
"""
user = models.ForeignKey(
User
)
host = models.CharField(
null=False, verbose_name="Host", max_length=128, unique=True
)
I'd like to use Django's generic views for doing CRUD operations on this. There is one field in this model that needs user input but the foreign key field doesn't need any user input. How can I exclude that field from the form that my generic view generates but assign it the value of the current authenticated user.
Thanks.
Have a look at Russel's answer to a similar question on the django-users group earlier this week.
Quoting the answer*:
Forms and Views solve different problems.
The View is solving the problem of "how do I handle this request and
convert it into a response?". The Form is solving the problem of "How
do I convert the POST data in this request into a model object (or a
change to a model object)?".
Very roughly, a view is doing the following:
View gets a request
View works out whether this is a GET or a POST
If its a POST, View asks the Form to turn the Post into a model change
Form returns success or failure
View responds to the success or failure of the Form.
View returns a response.
The functionality of the Form is a complete subset of the
functionality of the View -- and for this reason, it's a completely
interchangable internal component.
Now, in simple situations, it's possible for a View to guess all the
defaults for the form -- all it needs to know is that you're dealing
with a Foo model, and it can construct a default Foo ModelForm.
However, if you have more sophisticated form requirements, you're
going to need a customized Form.
We could have implemented this by exposing all the options of
ModelForm on the View class; but in order to keep everything clean, we
kept the ModelForm isolated, and provided the View with a way to
specify which Form class it's going to use.
So - to cover your use case of excluding fields, you define a
ModelForm that excludes the fields, then let the CreateView know the
form you want to use:
class CampaignForm(forms.ModelForm):
class Meta:
model = Campaign
exclude = ('user', 'name', 'content_inlined')
class CreateCampaignView(CreateView):
form_class = CampaignForm
template_name = "forms/create.html"
I'm guessing when you say "fix a values for a field", you mean setting
the values of user, name and content_inlined before you save the new
Campaign instance; to do this, you need to inject some extra code into
the form processing logic of the form:
class CreateCampaignView(CreateView):
form_class = CampaignForm
template_name = "forms/create.html"
def form_valid(self, form):
form.instance.user = ... (something meaningful.. e.g., self.request.user)
return super(CreateCampaignView, self).form_valid(form)
This overrides the default behavior when the form is valid, and sets
the extra values. The super() implementation of form_valid() will then
save the instance.
For the record, this could also be done by overriding the save()
method on the ModelForm -- however, if you do that, you lose the
request object, which you will need if you're trying to set the
instance values to something that is request-sensitive.
*the original answer set self.object.user instead of form.instance.user. This gives an AttributeError so I have changed it above.
I am a current web2py user, but find I still go back to Django once in a while (where I started). Specifically when working on projects where I want to make use of some specific django apps/plugins/extensions that don't yet exist in web2py.
One thing that I can't live without in web2py, which I am looking for a solution for in Django, is the way to create html forms from a db table and being able to then customize their look and layout in the view, without javascript.
Key things I am looking for:
Generate html form from a db table
Assign custom css classes/ids to each field in the generated html form (js disabled)
Place each form field/element in a pre-made html view via a method call in the view
i.e.
I have a table A. In web2py I can do (in controller):
def display_form():
form = SQLFORM(db.table_A)
#Can I do the following in Django? Assign custom CSS to each form field?
form.element(_name='email')['_class'] = = "custom_css_classes, list"
if form.accepts(request.vars, session):
response.flash = 'form accepted'
elif form.errors:
response.flash = 'form has errors'
else:
response.flash = 'please fill out the form'
return dict(form=form)
Then, in the View I can do:
form.custom.start
form.custom.widget.name
form.custom.widget.email
form.custom.widget.form_field_name
...
<div class="span-5 last"><input type="submit" class="register_btn" value="Sign Up"></input></div>
form.custom.end
The above takes a DB table, creates an HTML form, and then lets me stick each separate form field in any place in the pre-made HTML that I want (using those "custom" method calls on the passed "form" object. Including the custom css classes I assigned to each separate field of the generated html form.
See documentation for details on the above code:
http://web2py.com/book/default/chapter/06?search=define_table
http://web2py.com/book/default/chapter/07?search=sqlform#SQLFORM
http://web2py.com/book/default/chapter/05?search=#Server-side-DOM-and-Parsing
http://web2py.com/book/default/chapter/07?search=form.custom
How do I do the above in Django without dirtying my javascript with layout hacks. Assume javascript is disabled in the browsers where I need my app to run. Furthermore, I would love to make use of Django admin. Pylons solutions also welcome!
Links to articles/tutorials/howtos for this would be greatly appreciated.
Also, please make an equivalent result of the above code using the method you mention in your response...
Use ModelForm and override any field you wanna customize by explicitly declaring them.
If you want to set field attributes like class and id, you need to do something like this:
name = forms.CharField(
widget=forms.TextInput(attrs={'class':'special'}))
In case you are interested, you may change the order of the fields by specifying a fields sequence in your Meta class:
class Meta:
model = YourModel
fields = ('title', 'content')
You may read the full documentation here:
http://docs.djangoproject.com/en/dev/ref/forms/widgets/#django.forms.Widget.attrs
If you haven't already, take a look at Django's ModelForm. I am assuming that you have models mapped to the tables in question. Vanilla ModelForm instances will work without JS. However ModelForms are usually defined ahead of time and not constructed on the fly. I suppose they can be created on the fly but that would be a bit tricky.
I know that you can prepopulate admin form fields based on other fields. For example, I have a slug field that is automatically populated based on the title field.
However, I would also like to make other automatic prepopulations based on the date. For example, I have an URL field, and I want it to automatically be set to http://example.com/20090209.mp3 where 20090209 is YYYYMMDD.
I would also like to have a text field that automatically starts with something like "Hello my name is author" where author is the current user's name. Of course, I also want the person to be able to edit the field. The point is to just make it so the user can fill out the admin form more easily, and not just to have fields that are completely automatic.
I know that you can prepopulate some values via GET, it will be something like this
http://localhost:8000/admin/app/model/add/?model_field=hello
I got some problems with date fields but, maybe this could help you.
I recently used Django's ModelAdmin.get_form method for this purpose.
class MyModelAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
form = super(MyModelAdmin, self).get_form(request, obj, **kwargs)
form.base_fields['my_field_name'].initial = 'abcd'
return form
Yout should be careful about side effects as you are manipulating the base_fields directly.
Django's built-in prepopulated_fields functionality is hardcoded to slugify, it can't really be used for more general purposes.
You'll need to write your own Javascript function to do the prepopulating. The best way to get it included in the admin page is to include it in the inner Media class of a custom Form or Widget. You'll then need to customize your ModelAdmin subclass to use the custom form or widget. Last, you'll need to render some inline Javascript along with each prepopulated field to register the onchange handler and tell it which other field to populate from; I would render this via the custom Widget. To make it nice and declarative you could use a custom ModelAdmin attribute (similar to prepopulated_fields), and override ModelAdmin.formfield_for_dbfield to create the widget and pass in the information about what field it should prepopulate from.
This kind of admin hacking is almost always possible, but (as you can tell from this convoluted summary) rarely simple, especially if you're making an effort to keep your code nicely encapsulated.
I tried a few of these answers and none of them worked. I simply wanted to prepulate a field with another field from a related model. Taking this answer as a starting point, I finally tried to manipulate the model instance object (here obj) directly and it worked for me.
class MyModelAdmin(models.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
form = super(MyModelAdmin, self).get_form(request, obj, **kwargs)
if not obj.some_model_field:
obj.some_model_field = obj.related_model.prepopulating_model_field
return form
You can override the default django admin field by replacing it with a form field of your choice.
Check this :
Add custom validation to the admin
I would also like to have a text field
that automatically starts with
something like "Hello my name is
author".
Check out the docs at: http://docs.djangoproject.com/en/dev/ref/models/fields/#default
You could have a CharField() or TextField() in your model, and set this option, which will set the default text. 'default' can also be a callable function.
Something like:
models.CharField(max_length=250, default="Default Text")
The slug handling is done with javascript.
So you have to override the templates in the admin and then populate the fields with javascript. The date thing should be trivial, but I dont know how you should get the logged in users name to the script (not that I have thought very hard but you get the drift :).