WTForm with more than one model object - flask

I have a page that should create an instance of two distinct models at the same time, ie, when the same form is submitted.
I created a form using wtforms_alchemy but this covers only one model, not both:
class RoutineForm(ModelForm):
class Meta:
model = Routine
only = [u'name', u'comment']
field_args = {u'comment': {'widget': TextArea()}, }
I know I could create a form that does not inherit from the model and includes all the fields I need but for the sake of keeping things DRY how can I have an instance of ModelForm use two models?

You can use multiple forms at the same time. Give each a prefix to make sure the field names don't overlap in HTML. Validate both forms. Other than having two form objects, everything else is normal about the view and template.
class RoutineForm(ModelForm):
...
class EventForm(ModelForm):
...
#app.route('/make_event', methods=['GET', 'POST'])
def make_event():
routine_form = RoutineForm(prefix='routine')
event_form = EventForm(prefix='event')
if request.method == 'POST' and routine_form.validate() and event_form.validate():
...
return render_template('make_event.html', routine_form=routine_form, event_form=event_form)

Related

How to populate django form with selection from your models

I have a CreateSong CBV in Django that allows me to create song objects to a model. My question is, in the form I created for the view, how do I make the album column to be auto-populated with albums the user-created only? I get errors calling "self" that way.
See my views below
class CreateSong(CreateView):
model = Song
fields = [album, song_title]
fields['album'].queryset = Album.objects.filter(owner=self.request.user)
I think you should override get_form. See the example below:
class CreateSong(CreateView):
model = Song
fields = [album, song_title]
def get_form(self):
form = super().get_form()
form.fields['album'].queryset = Album.objects.filter(owner=self.request.user)
return form
You do not have access to self.request.user, because you are calling it at class level, thus when the class is being defined and not when the view is actually called. Instead you should override the get_form method as in Davit's answer.

Django 2 models 1 form

So, I'm still a total noob at Django and I was wondering how to do the following:
So lets say that I have something like the below code:
class UserProfile(models.Model):
#Some fields
class UserProfileOther(models.Model):
#Some other fields that I want in another table for organization
user_profile = models.OneToOneField(UserProfile)
How do I now create a form that includes both of the above models?
This kind of problem is what inline formsets are designed for.
You can create two separate ModelForm classes. But in your view, you have to add a prefix to their instances.
def viewname(request):
if request.method == 'POST':
form1 = forms.YourForm(request.POST, prefix="form1")
form2 = forms.YourOtherForm(request.POST, prefix="form2")
if form1.is_valid() and form2.is_valid():
# process valid forms
else:
form1 = forms.YourForm(prefix="form1")
form2 = forms.YourOtherForm(prefix="form2")
....
Using a prefix ensures that fields with similar names are not mixed up.
The way I did it was to create a form based on the first model, and then to create a form based on the second model that inherits the first form. Meaning:
class UserProfileForm(ModelForm):
...
class UserProfileOtherForm(UserProfileForm):
...
And pass the UserProfileOtherForm to form template. It worked for me. Not sure if there is a simpler approach.

django: use a queryset as modelform initial data

I'm making a settings interface which works by scanning for a settings folder in the installed applications, scanning for settings files, and finally scanning for ModelForms.
I'm at the last step now. The forms are properly found and loaded, but I now need to provide the initial data. The initial data is to be pulled from the database, and, as you can imagine, it must be limited to the authenticated user (via request.user.id).
Keep in mind, this is all done dynamically. None of the names for anything, nor their structure is known in advanced (I really don't want to maintain a boring settings interface).
Here is an example settings form. I just pick the model and which fields the user can edit (this is the extent to which I want to maintain a settings interface).
class Set_Personal_Info(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('nick_name', 'url')
I've looked at modelformset_factory which almost does what I want to do, but it only seems to work with results of two or more. (Here, obj is one of the settings forms)
Formset = modelformset_factory(obj.Meta.model, form=obj)
Formset(queryset=obj.Meta.model.objects.filter(id=request.user.id))
I can't filter the data, I have to get one, and only one result. Unfortunately I can't use get()
Formset = modelformset_factory(obj.Meta.model, form=obj)
Formset(queryset=obj.Meta.model.objects.get(id=request.user.id))
'User' object has no attribute 'ordered'
Providing the query result as initial data also doesn't work as it's not a list.
Formset = modelformset_factory(obj.Meta.model, form=obj)
Formset(initial=obj.Meta.model.objects.get(id=request.user.id))
'User' object does not support indexing
I have a feeling that the answer is right in front of me. How can I pull database from the database and shove it into the form as initial values?
I'm not really sure I understand what you're trying to do - if you're just interested in a single form, I don't know why you're getting involved in formsets at all.
To populate a modelform with initial data from the database, you just pass the instance argument:
my_form = Set_Personal_Info(instance=UserProfile.objects.get(id=request.user.id))
Don't forget to also pass the instance argument when you're instantiating the form on POST, so that Django updates the existing instance rather than creating a new one.
(Note you might want to think about giving better names to your objects. obj usually describes a model instance, rather than a form, for which form would be a better name. And form classes should follow PEP8, and probably include the word 'form' - so PersonalInfoForm would be a good name.)
Based on what I've understand ... if you want to generate a form with dynamic fields you can use this:
class MyModelForm(forms.ModelForm):
def __init__(self, dynamic_fields, *args, **kwargs):
super(MyModelForm, self).__init__(*args, **kwargs)
self.fields = fields_for_model(self._meta.model, dynamic_fields, self._meta.exclude, self._meta.widgets)
class Meta:
model = MyModel
Where dynamic_fields is a tuple.
More on dynamic forms:
http://www.rossp.org/blog/2008/dec/15/modelforms/
http://jacobian.org/writing/dynamic-form-generation/
http://dougalmatthews.com/articles/2009/dec/16/nicer-dynamic-forms-django/
Also Daniel's approach is valid and clean ... Based on your different ids/types etc you can you use different Form objects
forms.py
class MyModelFormA(forms.ModelForm):
class Meta:
model = MyModel
fields = ('field_a','field_b','field_c')
class MyModelFormB(forms.ModelForm):
class Meta:
model = MyModel
fields = ('field_d','field_e','field_f')
views.py
if request.method == 'POST':
if id == 1:
form = MyModelFormA(data=request.POST)
elif id == 2:
form = MyModelFormB(data=request.POST)
else:
form = MyModelFormN(data=request.POST)
if form.is_valid():
form.save() else:
if id == 1:
form = MyModelFormA()
elif id == 2:
form = MyModelFormB()
else:
form = MyModelFormN()

Django user HiddenInput vs. saving directly in views with Class Based Views

Please this as a consideration question. Maybe somebody will use one of the
solutions below.
I have a couple of models which contain a ForeignKey(User) field.
My class-based create views are derived from the generic CreateView.
There are two options to save the associated user when adding a new object:
Saving the form in the views by overriding the form_valid method;
this doesn't expose user_id (and other not mentioned here data that should not be exposed)
class CreateOfferView(CreateView):
model = Offer
form_class = SomeModelFormWithUserFieldExcluded
def form_valid(self, form):
instance = form.save(commit=False)
instance.user = self.request.user
instance.save()
Saving the form with user id stored (and exposed) in a hidden field.
Here's the tricky part. There are more models with user field... so
when creating a form I need to fill the user field with initial (currently logged in) user and also I need to make that field hidden. For this purpose I've used my OwnFormMixin
class OwnFormMixin(object):
def get_form(self, form_class):
form = super(OwnFormMixin, self).get_form(form_class)
form.fields['user'].widget = forms.HiddenInput()
def get_initial(self):
initial = super(OwnFormMixin, self).get_initial()
initial['user'] = self.request.user.pk
#I could also do this in get_form() with form.fields['user'].initial
class CreateOfferView(OwnFormMixin, CreateView):
model = Offer
form_class = SomeModelFormWithAllFields
There are more CreateXXXView using the OwnFormMixin..
How do you save your user data in the forms?
Hidden vs. saving directly in your views? What are pros/cons?
Unless you're allowing users to modify that ForeignKeyField, there's no reason to include it in a form — I'd go with your first solution of using exclude to keep the user field out of your ModelForm, and setting the user from request.user. In fact, the Django documentation now has an example along these exact lines.
You have the advantage of not having to secure against manipulation of the user_id parameter, not exposing your internal user IDs and not having to worry about the different Create vs. Update cases. A slight disadvantage is that if you ever need the ability to change an object's associated User you'll need to start again.

Django forms "not" using forms from models

I have a form generated from various models and the various values filled go and sit in some other table. Hence, in this case I haven't used the inbuilt Django forms(i.e. I am not creating forms from models ).
Now the data which is posted from the self made form is handled by view1 which should clean the data accordingly. How do I go about it and use the various functions clean and define validation errors (and preferably not do validation logic in the view itself!)
EDIT:
I have 3 models defined ==> 3 db tables. Now a form is to be created which shows data from 2 of the models and then the data from this form is to be saved in the 3rd table! In this scenario, I have created the form myself and I want to use form functionalities to validate the inputs of this self-made form. How should I go about it? In case, I cannot use the inbuilt form functionalities, where and how do i validate this self-made form(not using form from models)
I'm still not sure why you couldn't use built-in form validation methods.
Assume models:
class A(models.Model):
a = models.CharField()
class B(models.Model):
b = models.CharField()
class C(models.Model):
c = models.CharField()
d = models.CharField()
Assume that values from A.a and B.b need to end up in C.c and C.d model through form:
class MyForm(forms.Form):
a = forms.CharField()
b = forms.CharField()
When you populate and submit your form do a standard validation on it:
if request.method == "POST":
form = MyForm(request.POST)
if form.is_valid():
model3 = C() # create 3rd model objects
model3.c = form.cleand_data['a'] # and assign values from form to it
model3.d = form.cleand_data['b']
model3.save() # save the data into the 3rd table
Or you could use model validation instead of form validation but it's more or less the same principle.
Or am i still not reading your question correctly? Example code is always welcomed.
The only interaction the view should have with the form is to control when the data is validated, and what to do if it is or is not valid, as in,
if form.is_valid():
do_something()
Otherwise everything should be done in the form class, using the clean_fieldname() and clean() methods. See http://docs.djangoproject.com/en/dev/ref/forms/validation/ for more info on how to define these within a form.