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.
Related
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)
My issue is that my app is not allowing me to update a OneToOneField field. Here's my explanation of what I'm trying to do.
I am building an inventory app that keeps track of instruments that have been loaned to students. There will always be a one-to-one database relationship between students and instruments. So an individual student can't ever have more than one instrument and vice versa.
I therefore created an Intrument model that looks like this:
class Instrument(models.Model):
instrument_type = models.CharField(max_length=100)
needs_repairs = models.BooleanField()
inventory_id = models.CharField(max_length=100)
student = models.OneToOneField(Student, null=True, blank=True, default = None)
I have created a form that allows me to update existing students, and I'm trying to use as much built-in stuff as possible so that I don't need to re-write validation code or HTML. So I'm using a ModelForm object and validating my input using the is_valid() method.
Here's an example of a POST request to update an instrument:
csrfmiddlewaretoken=xyUBhVuQZus6XmeV2DhCmpJHwIXVmdHm&instrument_type=Viola&inventory_id=abcde&student=3
Please note that the only field with a uniqueness constraint is student.
So finally, here's the problem: when I call the is_valid() method it always fails with an error saying that the student has already been assigned to an instrument.
My first thought was to use the framework to add some pre-validation code that didn't error if the student pkey didn't change. This certainly seems easy enough, but it seems to be a bit hacky to me. I assumed that one-to-one relationships would "just work" like all of the other Model fields and that no special validation would be required.
But then I read the API docs for the OneToOneField class and it doesn't seem to address one-to-one database relationships - it seems to address one-to-one OO relationships. So I may be using the wrong Model field type all together. And since this is such a simple app, I'm not performing a ton of OO modeling - I'm just worried about proper data modeling :-)
So am I using the wrong field, or is the "proper" way to fix this to add pre-validation code to my Student model?
Updates From Comments
Here's the closest thing that I have to a stack trace:
>>> data = {'instrument_type': 'Viola', 'inventory_id': 'abcde', 'student': 3, 'repairer': 1}
>>> form = InstrumentForm(data)
>>> form.is_bound
True
>>> form.is_valid()
False
>>> form.errors
{'student': [u'Instrument with this Student already exists.']}
I use a single view method to display Instrument detail and update a single Instrument. Here's that:
def instrument_detail(request, instrument_id):
try:
instrument = Instrument.objects.get(pk=instrument_id)
except Instrument.DoesNotExist:
raise Http404
# Default if not a POST
form = InstrumentForm(instance=instrument)
if request.method == 'POST':
form = InstrumentForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('instruments.views.instruments_index'))
# otherwise...
t = loader.get_template('instruments/details.html')
c = RequestContext(request, {
'instrument': instrument,
'form': form,
})
return HttpResponse(t.render(c))
You're not passing the instance when instantiating the form on POST.
if request.method == 'POST':
form = InstrumentForm(request.POST, instance=instrument)
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.
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()
one of the forms I need is a composite of simple fields (say "Department", "Building" and "RoomNumber"), and of dynamically generated pairs of fields (say "Name" and "Email"). Ideally, editing the contents of the simple fields and adding/removing dynamic field pairs would be done on a single form.
Code-wise, I'm wondering if trying to embed a Formset (of a form with the two dynamic fields) as a field in an ordinary form is a sensible approach or if there's another best practice to achieve what I'd like to accomplish.
Many thanks for any advice on these matters,
I'm not sure where the idea that you need to "embed a Formset as a field" comes from; this sounds like a case for the standard usage of formsets.
For example (making a whole host of assumptions about your models):
class OfficeForm(forms.Form):
department = forms.ModelChoiceField(...
room_number = forms.IntegerField(...
class StaffForm(forms.Form):
name = forms.CharField(max_length=...
email = forms.EmailField(...
from django.forms.formsets import formset_factory
StaffFormSet = formset_factory(StaffForm)
And then, for your view:
def add_office(request):
if request.method == 'POST':
form = OfficeForm(request.POST)
formset = StaffFormSet(request.POST)
if form.is_valid() && formset.is_valid():
# process form data
# redirect to success page
else:
form = OfficeForm()
formset = StaffFormSet()
# render the form template with `form` and `formset` in the context dict
Possible improvements:
Use the django-dynamic-formset jQuery plugin to get the probably-desired "add an arbitrary number of staff to an office" functionality without showing users a stack of blank forms every time.
Use model formsets instead (assuming the information you're collecting is backed by Django models), so you don't have to explicitly specify the field names or types.
Hope this helps.