Using two models for a form in Django - django

I've run into a problem with using two models for my form in Django. I have two models, Animal and Family, both with a "name" field.
I use the two modelforms on my template for my form, and when it's submitted, the POST data for 'name' only returns one value.
Short of renaming the 'name' field in my models, is there any way around this?
Thanks for your help. This is my first post here.

You could make use of the prefix-argument when initializing the modelforms;
animal_form = AnimalForm(request.POST or None, prefix="animal")
family_form = FamilyForm(request.POST or None, prefix="family")
Which will output something like;
<input id="id_animal-name" type="text" />
<input id="id_family-name" type="text" />

You can get the different POST values that are under the same name with request.POST.getlist.
However, the correct value will then depend on the position of the input field in the form (I guess) and this really can't be a good idea. Change the name of the field, not in the model, but in your form class (I hope you use one).

Related

Specifying specific form format in Django

I am using materializecss to give my django site some material elements. I have put together a form (the 'old' way using html) but now realised I need to use a django form instead. The problem is, these forms don't play well with materialises built in column system (they use classes to determine rows and column spacing). Here is an example of the layout I set up so far. However when defining the form through form.py, it spits out one input per layer.
My question is: what can I do to either a) get django to work with the html-defined form or b) make a 'form template' to give the input fields the appropriate classes?
If you want to see the code I can post some but I'm quite a new coder so it's messy.
Thanks!
There are three ways I can think of off the top of my head.
If you want full control over the HTML form, in a Django template or HTML form, simply map the names of your fields to match the underlying field names in the Django form. This way, when POSTed back to your view, Django will automatically link up the POSTed fields with the Django form fields.
For example, if you have a field username in your Django form (or Django model if using ModelForm), you could have an element <input type="text" name="username" maxlength="40"> (that you can style any way you need) on your HTML form that Django will happily parse into your Django form field, assuming your view is plumbed correctly. There is an example of this method in the Django documentation.
Another way is to customize the Django form field widgets in your Django form definition. The Django documentation talks a little bit about how to do this. This is great for one offs, but is probably not the best approach if you expect to reuse widgets.
The final approach would be to subclass Django form field widgets to automatically provide whatever attributes you need. For example, we use Bootstrap and have subclassed nearly all of the widgets we use to take advantage of Bootstrap classes.
class BootstrapTextInput(forms.TextInput):
def __init__(self, attrs=None):
final_attrs = {'class': 'form-control'}
if attrs is not None:
final_attrs.update(attrs)
super().__init__(attrs=final_attrs)
Then it's simply a matter of letting the Django form know which widget to use for your form field.
class UsernameForm(forms.ModelForm):
class Meta:
model = auth.get_user_model()
fields = ['username']
widgets = {'username': BootstrapTextInput()}
Hope this helps. Cheers!

How to get the order value of a form within template (django)?

I am using formset for my project. I have several form in my formset. Now I want to customize the appearance of form. I want to do this by using the order value of each form. One example of the input for an "ORDER" of form is shown below:
<input type="text" name="phones-0-ORDER" value="1" id="id_phones-0-ORDER">
I want to get the value(value="1" in this case) of this input.
I have generated the formset from my models directly using the inlineformset_factory in my view.
https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#using-an-inline-formset-in-a-view
At the creation of my formset, I have used the following code:
PhoneNumberFormSet = inlineformset_factory(Patron, PhoneNumber, can_order=True)
In this way, every form in the formset will have an order. Let's say we have 3 forms in the formset, the first form will hold the order 1, the second order 2, the third order 3.
I want to use the "order" of the form in my template to control a loop.
Anyone knows how to get the order value in template?
For your information, the type of ORDER is IntegerField. So my question is equal to "how to get the initial(pre filled-in) data of an IntegerField in template.
Thanks for your answers!
I believe you are asking how to set initial data within a formset. If that is the case, you will find the following information valuable within the django docs:
https://docs.djangoproject.com/en/dev/topics/forms/formsets/#using-initial-data-with-a-formset
If you are actually asking about how to get to the values from the client side, you would want to do so with jquery selectors.
http://api.jquery.com/category/selectors/
{% if form.can_order %}
{{ form.ORDER }}
{% endif %}

Django access field's value dynamically by keyword argument to pass to template

I want to create an edit form for a single field in my model, where the textarea is prefilled with the current value for that field. However, the exact field name is not hardwired, and I want it to be specified by the url.
My model is called Topic. Two example fields are Notes and Objectives. I can hardwire the field value as in the following:
urls.py
(r'^/(?P<topicshortname>\d+)/(?P<whichcolumn>[^/]+)/edit/$', 'mysyte.myapp.views.edit_topic_text'),
views.py
def edit_topic_text(topicshortname, whichcolumn):
thetopic = Topic.objects.get(topic__shortname__iexact = topicshortname)
content = Topic.objects.get(topic__shortname__iexact = topicshortname).objective
return render_to_response('topic_text_edit.html', locals())
topic_text_edit.html
<form method="post" action="../save/">
<textarea name="content" rows="20" cols="60">{{ content }}</textarea>
<br>
<input type="submit" value="Save"/>
</form>
I can also do the hardwiring in the template by using {{ thetopic.objective }}, but if I visited http://mysite.com/topic/Notes/edit/ both of these would prepopulate the form with the objective value, not the notes value.
Can I use the 'whichcolumn' url argument to specify which field to update in the object?
You can use getattr to get the value of an attribute by name. For your example:
def edit_topic_text(topicshortname, whichcolumn):
thetopic = Topic.objects.get(topic__shortname__iexact = topicshortname)
content = getattr(thetopic, whichcolumn)
return render_to_response('topic_text_edit.html', locals())
However, you should be also aware of the security implications of this. Users will be able to edit any field on the model they like by changing the url.
You should either check the value of whichcolumn before doing anything else with that data, or limit the possibilities in the urlconf with a more specific regular expression, eg:
(r'^/(?P<topicshortname>\d+)/(?P<whichcolumn>(Notes|Objectives))/edit/$', 'mysyte.myapp.views.edit_topic_text'),
You also mentioned fields 'Notes' and 'Objectives' but are accessing the field 'objective', so you may need to map the values of whichcolumn to the field name you are interested in, eg:
(r'^/(?P<topicshortname>\d+)/Objectives/edit/$', 'mysyte.myapp.views.edit_topic_text', {'whichcolumn': 'objective'}),
(r'^/(?P<topicshortname>\d+)/Notes/edit/$', 'mysyte.myapp.views.edit_topic_text', {'whichcolumn': 'note'}),
Another thing you should be aware of is that you where accessing the database twice by calling Topic.objects.get(...) twice. You should reuse the value of thetopic.
you should separate the two concepts of Notes and Objectives in two different classes, then use them in your Topic main class as reference
it would be easier for you to retrieve your object type and populate the correct one

Django fields in django-registration

I am setting up django registration, and I came across this piece of code in the RegistrationForm --
attrs_dict = { 'class': 'required' }
email = forms.EmailField(widget=forms.TextInput
(attrs=dict(attrs_dict, maxlength=75)),
label='Email')
What does the part (attrs=dict(attrs_dict, maxlength=75)) mean/do? I know what the maxlength part does but was unclear what the creation of a dictionary is doing, and what the attrs_dict is doing. Any explanation of this piece of code would be great. Thank you.
A bit of test showed that dict(attr_dict, maxlenght=75) equals to
{'class': 'required', 'maxlength':75}
So when the email filed is rendered to an html element, 2 attributes, class and maxlength will be added to the label.
It is creating a dictionary of attributes which will be required to add validation kind of things in finally rendered form, in this way we dont need to do anything in the template code to add validation and security.
Each form field in django uses a widget. You can either specify it during field creation or a default widget is used.
Here you are specifying widget TextInput on EmailField
(attrs=dict(attrs_dict, maxlength=75)) becomes:
{'class': 'required', 'maxlength':75}
Now these will be present as attributes in the rendered html for this widget. So, rendered html for field email would look like:
<input id="id_email" type="text" class="required" maxlength="75" name="email" />

In Django, how do you save multiple form fields into a single model field as a list of values using a ModelForm?

Given:
from django.db import models
class MyModel(models.Model):
...
owners = models.CharField(max_length=255, blank=False)
where owners is a pipe-separated list of email addresses.
In the page, there are multiple <input> fields with the same name, so the server gets the values in an array.
The HTML code for the form was done by-hand and doesn't use built-in templates (such as form.as_p).
Can Django's ModelForms handle something like this?
What is the proper location to handle the data transformation, both when retrieving the model (do I have to make a custom models.Manager?) and when saving it (which save() method do I override? the Model's or the ModelForm's)?
--UPDATE FOR CLARIFICATION--
In the database:
+-----+---------------------------+-----+
| ... | owners | ... |
+-----+---------------------------+-----+
| ... | "a#a.com|b#b.com|c#c.com" | ... |
+-----+---------------------------+-----+
The form:
<form ... >
...
<input type="text" name="owners" />
<input type="text" name="owners" />
<input type="text" name="owners" />
...
</form>
You can use forms.MultiValueField and forms.MultiWidget. MultiValueField knows that your model's one field contains multiple discrete values and the MultiWidget widget knows how each discrete value should be presented. They'll handle creating unique names for your fields so that they can reconstruct the form from POST values and you won't have to override any form methods that provide implementation, i.e. you won't have to override save().
As you can see, the official docs are a bit thin on the topic. I suggest you take a peek at the code to figure out how everything is wired and what gets called when, since your requirements are always going to need a unique solution, i.e: how many emails will the field contain? Exactly two, so I can specify two CharField widgets by hand, or is it going to be between 0-2, so I have to dynamically construct everything depending on the value passed? If you need a good example as a starting reference, check out this SO question which contains a clean and easy to follow implementation.
Ideally, you'll want to have a PipedEmailAddressesField that extends CharField as your model's field, one that knows that it's storing piped email addresses. It should be able to construct it's own appropriate MultiValueField form field which should in turn construct it's own appropriate MultiWidget field.
Here is an example of saving a modelform in a view:
http://themorgue.org/blog/2008/05/14/django-and-modelform/
You can get the extra fields manually using request.POST['fieldname']
Hope this helped.