submit multiple forms synchronously in django - django

Is there a way to submit simultaneously two forms connected through a foreign key in django?
I need to send them in order, so the best idea is to use synchronous option, right?
Any ideas? A simple example would be very useful!
Edit: Each form is on a different tab in the template. I have 5 tabs so I have five form tags.

You can post 2 forms at the same time using prefixes. Let's say you have these models:
Post => Category and you want to save a post and a category at the same time.
In your view:
post_form = PostForm(prefix="post", ...)
category_form = CategoryForm(prefix="category", ...)
...
if post_form.is_valid() and category_form.is_valid():
...
In your template you just show them within the same form tag.

Related

Django Forms: How to create a simple dropdown list in a form using values from an existing model

I'm new to Django and struggling to find the best/correct way to populate a dropdown list in a ModelChoiceField within a form using a list I have stored in a database.
The basic code below works fine with my view passing the form as context to my template but the drop-down returns a 'dictionary' like list with the ugliness or the brackets, colons and the key name (see the attachment)
from django import forms
from .models import constituency
class ConstituencyForm(forms.Form):
ConstituencySelection = forms.ModelChoiceField(queryset=constituency.objects.all().values('name'))
My questions are:
1/ The purpose of this page will be to select one of 650 areas from the dropdown list and have it highlighted on the map. Is this an appropriate way to approach the task (using a queryset within the form itself)?
2/ How do I clean up this list, it should simply read:-
Aldershot
Aldridge-Brownhills
Altrincham and Sale West
and so on and so on
Your help would be forever appreciated!
Thanks,
Phil #anoobinneed
Using values_list() with flat=True will do it.
Change:
queryset=constituency.objects.all().values('name')
To:
queryset=constituency.objects.all().values_list('name', flat=True)

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!

Django formwizard with formsets and forms on same page

I'm working with Django 1.4 FormWizard (specifically, NamedUrlFormWizard)
first, the basics. i have a 3 step form wizard i'm building. The final outcome is along the lines of defining a template, and then choosing some people to use it, and then send them an email.
Step 1 - enter in basic template data (name, description, etc)
Step 2 - define a list of N fields, each with their own set of attributes but all identical in structure
Step 3 - choose one or more users to email, AND customize the contents of this email before saving
so far, in the form wizard:
Step 1 is a standard Form class, and works just fine.
Step 2 is a standard Formset class, and works just fine.
Step 3 is giving me some trouble. It needs to be a formset (list of email addresses), but also an additional form input field with email text. I can't figure out how to have both a formset in addition to a non-repeating form input on the same page inside of a form wizard.
in a perfect world, i could define a Formset as just another form field in a Form definition.
ie:
class EmailAddressForm(forms.Form):
email = forms.EmailField()
class EmailAddressesAndText(forms.Form):
emailText = forms.Textarea()
emailAddressFormSet = formset_factory(EmailAddressForm, etc etc)
then point my FormWizard page at 'EmailAddressesAndText' and be done with it. but its not a perfect world. Any ideas on how I can achieve such a thing?
You can use get_form() method of WizardView to customize the form for particular step. Refer: WizardView.get_form. This answer can help you to add a field
Another option would be to add the field in formset and through JS disable and hide all instances other than first one.

Django - Confirmation page before change of different models

I'd like to create a confirmation page for selected objects before a change is made to them (outside the admin). The objects can be of different models (but only one model a time).
This is much like what is done in administration before deletion. But the admin code is complex and I haven't grasped how it is done there.
First I have severall forms that filter the objects differently and then I pass the queryset to the action / confirmation page. I have created a form factory so that I can define different querysets depending on model (as seen in another similiar question here at Stackoverflow):
def action_factory(queryset):
''' Form factory that returns a form that allows user to change status on commissions (sale, lead or click)
'''
class _ActionForm(forms.Form):
items = forms.ModelMultipleChoiceField(queryset = queryset, widget=forms.HiddenInput())
actions = forms.ChoiceField(choices=(('A', 'Approve'), ('D' ,'Deny'), ('W' ,'Under review'), ('C' ,'Closed')))
return _ActionForm
Which I use in my view:
context['form']=action_factory(queryset)()
The problem is that the items field wont be displayed at all in the html-code when it is hidden. When I remove the HiddenInput widget it displays the form correctly.
I don't want to display the choice field since there can be thousands of objects. All I want to have is something like "Do you want to change the status of 1000 objects" and a popdown and a submit button. A simple enough problem it seems, but I can't get it to work.
If someone has a solution to my current attempt I would be glad to hear how they have done it. Even better would be if there is a cleaner and better solution.
I used the wrong widget. It should be MultipleHiddenInput not HiddenInput.

Limiting Django ModelChoiceField queryset to selected items

Here is what I've been struggling for a day...
I have a Message model in which recipients is a ManyToManyField to the User model.
Then there is a form for composing messages. As there are thousands of users, it is not convenient to display the options in a multiple select widget in the form, which is the default behavior. Instead, using FcbkComplete jquery plugin, I made the recipients field look like an input field where the user types the recipients, and it WORKS.
But...
Although not visible on the form page, all the user list is rendered into the page in the select field, which is something I don't want for obvious reasons.
I tried overriding the ModelChoiceField's behavior manipulating validation and queryset, I played with the MultipleChoice widget, etc. But none of them worked and felt natural.
So, what is the (best) way to avoid having the whole list of options on the client side, but still be able to validate against a queryset?
Have you seen django-ajax-selects? I've never used it, but it's in my mental grab bag for when I come across a problem like what it sounds like you're trying to solve...
I would be trying one of two ways (both of which might be bad! I'm really just thinking out aloud here):
Setting the field's queryset to be empty (queryset = Model.objects.none()) and having the jquery tool use ajax views for selecting/searching users. Use a clean_field function to manually validate the users are valid.
This would be my preferred choice: edit the template to not loop through the field's queryset - so the html would have 0 options inside the select tags. That is, not using form.as_p() method or anything.
One thing I'm not sure about is whether #2 would still hit the database, pulling out the 5k+ objects, just not displaying them in the html. I don't think it should, but... not sure, at all!
If you don't care about suggestions, and is OK to use the ID, Django Admin comes with a raw_id_field attribute for these situations.
You could also make a widget, that uses the username instead of the ID and returns a valid user. Something among the lines of:
# I haven't tested this code. It's just for illustration purposes
class RawUsernameField(forms.CharField):
def clean(self, value):
try:
return User.objects.get(username=value)
except User.DoesNotExist:
rause forms.ValidationError(u'Invalid Username')
I solve this by overriding the forms.ModelMultipleChoiceField's default widget. The new widget returns only the selected fields, not the entire list of options:
class SelectMultipleUserWidget(forms.SelectMultiple):
def render_options(self, choices, selected_choices):
choices = [c for c in self.choices if str(c[0]) in selected_choices]
self.choices = choices
return super(SelectMultipleUserWidget,
self).render_options([], selected_choices)
class ComposeForm(forms.Form):
recipients = forms.ModelMultipleChoiceField(queryset=User.objects.all(),
widget=SelectMultipleUserWidget)
...