I am trying to create an edit form for my model. I did not use a model form because depending on the model type, there are different forms that the user can use. (For example, one of the forms has a Tinymce widget, while the other doesn't.)
Is there any way of setting the initial data of a form (not a ModelForm) using a model?
I tried the following but getting an error:
b = get_object_or_404(Business, user=request.user)
form = f(initial = b)
where f is a subclass of forms.Form
The error I am getting is AttributeError: 'Business' object has no attribute 'get'
The initial data needs to be a dict (or at least have a dict-like interface, which a Django model does not have).
You can construct a dict from your model using django.forms.models.model_to_dict:
from django.forms.models import model_to_dict
b_as_dict = model_to_dict(b)
This is the same function Django's built in ModelForm class uses to set its initial data, and while you've specified that you don't want to use a ModelForm it may be more convenient to find some way of structuring your code that allows you to do so.
There is a more direct way of using model data than dict. See sample in documentation.
b = get_object_or_404(Business, user=request.user)
form = f(instance = b)
Related
Consider the following code:
views.py
class BHA_UpdateView(UpdateView):
model = BHA_overall
pk_url_kwarg = 'pk_alt'
form_class = BHA_overall_Form
To my understanding, pk_url_kwarg = 'pk_alt' will query and return instances of model = BHA_overall.
Is there any way that I can force pk_url_kwarg to query
& return other model instances defined in models.py (like model = other_model), while having my get_object() method to return objects in model = BHA_overall? What CBV should I use (I think UpdateView is not a good choice in this case)?
++ I'm trying to make a page that allows users to manage information about the product they use. So, ultimately I will implement forms, and the user input needs to be saved in DB
++ I need pk_url_kwarg = 'pk_alt' to query other models and generate url. But I still need get_object() method to return objects in model = BHA_overall to generate form fields on the user side.
From my understanding you need a django form generated from BHA_overall, but the data should be saved to AnotherModel right?
I will propose 2 solutions to this problem, Choose what best fits you.
Multiple views:
Have multiple views for the task, What I mean is create a view which creates the form for the frontend using BHA_overall, you can create both Create and Update view this way and update view's initial could be overwritten so form will have expected value when editing. And now post the data to another view which handles the post data. This view can have your AnotherModel doing its thing.
Using Django Form:
If you dont like having multiple views, You can keep things simple by creating a form yourself. Create a DjangoForm with the same fields you want to show to the user and use it in to create your own views, Now you wont need BHA_overall and use your AnotherModel to save datal.
I have a couple of forms, one is created from as a ModelForm and the other is a simple Form. Both of them are used in a request.POST, and to obtain the information from them I am using to different methods:
For the ModelForm form, I do this:
form = ApplicantForm(request.POST)
if form.is_valid():
applicant = form.save(commit=False)
applicant.confirmation_code = '999999'
applicant.save()
For the simple form, I am using:
form = ConfirmationCode(request.POST)
code = request.POST['confirmation_code']
confirmation_id=request.POST['confirmation_id']
As you can see, to access the information in the first form I am using the "form.save.ANYFIELD", and for the second one I am using "request.POST['ANYFIELD']. Is it possible to access the the information in the first form using the request.POST methods even if it hasnt been saved? Which is better?
You can try like this for modelform:
form = ApplicantForm(request.POST)
if form.is_valid():
app_code= form.cleaned_data['confirmation_code'] #assuming confirmation_code is a field in your modelform
.....
You seem a bit confused about what saving is doing in a modelform. When you call form.save(), you're creating a new instance of the model the form is associated with, and (unless you specify commit=False) saving that data to the database. Because you have an instance, you can use any of the normal model instance methods and access patterns.
If you want to use a form without an associated model, you can't call save - because there's nothing to save, and no model to create an instance of - but you should access the data via the form.cleaned_data dictionary after you call form.is_valid(). This is because the data in cleaned_data has been validated according to the rules in the form, and converted into the relevant types where necessary: for instance, if you had an IntegerField in your form called my_number, request.POST['my_number'] will be a string like "3" but form.cleaned_data['my_number'] will be an actual integer, 3.
I thought it would be easier to start using models form instead of regular forms(giving up on all the easy things modelform provides).
But when I try to do this:
>>> m = Model.objects.get(pk=1)
>>> f = ModelForm(instance=m)
>>> f.is_valid()
False
>>> f.save()
AttributeError: 'ModelForm' objects has no attribute 'cleaned_data'
I think django documentation is wrong by saying:
Every form produced by ModelForm also has a save() method. This
method creates and saves a database object from the data bound to
the form. A subclass of ModelForm can accept an existing model
instance as the keyword argument instance; if this is supplied, save()
will update that instance. If it’s not supplied, save() will create a
new instance of the specified model
Cause this is just not working for me.
Am I right that the django documentation is wrong?
Thanks in advance.
You may have forgotten to add "data" into the ModelForm instantiation according to the user's request.POST data.
f = ModelForm(data=request.POST, instance=m)
f.is_valid() # is True if the data is ok.
In any case, it would be better to post your relevant code: The model class, the model form class and your view.
EDIT: you must add a data= parameter (or the first parameter, if you don't name it) to a ModelForm initialization, if you want is_valid() to work. is_valid is here to check the given data against the various validation rules, and only if it's ok, lets you save the ModelForm. Initializing a ModelForm just with the instance= named parameter does not trigger any validation, because there is nothing new to validate.
I have a form that consists of a response for each entry in another model. At the time the form is generated the response to each item may or may not exist. I need a form that allows me to update the response if it exists and create it with the form post data if it doesn't exist.
Currently I am iterating through a range and creating my forms with the post data:
forms = [SpecialNoteForm(request.POST, prefix=str(x), ) for x in rang(1,3)]
I am doing this because I don't know how else to access the form data cleanly in order to identify the object that the form should be instantiated with. I tried doing something like this after the forms list was created because i can then access the form data:
for form in forms:
try:
instance = SpecialNote.objects.get(flag=form["flag"].data, host=form["host"].data)
form.instance = instance
form.save()
The errors on the form persist after I do this, however. I need a way of accessing the data I need to instantiate the object at the time of form creation or a way of re-evaluating the form after i've attached an instance to it.
EDIT
I ran into the same problem with model formsets as I did with my initial approach--I don't know how to instantiate the forms while at the same time allowing for intial values on forms that don't have an instance. I don't want to create all of the model instances before hand because it is import whether or not the user has submitted these with the required fields filled in.
My current approach is still using the model forms:
forms = []
for n in form_range(request.POST): # calculates number of forms based on post data
try:
instance = SpecialNote.objects.get(flag=request.POST.get('%s'%n+'-flag'), host=request.POST.get('%s'%n+'-host'))
except:
instance = None
forms.append(SpecialNoteForm(request.POST, prefix=str(n), instance=instance))
for form in forms:
if form.is_valid():
form.save()
In summary, the problem with formsets is I don't know how to properly instantiate the forms without having them be queriable, i.e. already in the database. The problem with using regular model forms and a prefix is that getting the objects that i need to instantiate them with is messy (as you can see from my current approach). I'm looking for a solution to either of these two problems.
Multiple identical model forms on one page is what model formsets are for. They should take care of all of those issues.
I have a multiple ModelForm classes that each represent a different Model. I would like to have a generic 'create' function that loads the specified model form based on a URL parameter. It is possible to load a model dynamically with this:
model_name = 'TestModel'
m = get_model('AppLabel', model_name)
Does anyone know how I can achieve the same for ModelForms, something like:
modelform_name = 'TestModelForm'
f = get_form('AppLabel', modelform_name)
if f.is_valid():
...
I can not think of a way to do this with generic views - they require the ModelForm to be passed, rather than just its name. If I get the model with get_model then pass that to the generic view it will display a form but I am unable to exclude model fields.
TIA for any tips
When you create a ModelForm it does not register itself with its model's app. (Based on experience and a quick browse through the source).
Here are some otheroptions I can think of:
All ModelForm classes exist in a single module: Use getattr on that module based on the string.
ModelForm's are spread out among many models and you have a reasonable (<30) amount of forms:
Create a dictionary mapping from form strings you expect to ModelForm classes. For example:
from some_app.forms import FirstModelForm
from another_app.forms import SecondModelForm
from additional_app.forms import FirstModelForm as AdditionalAppFirstModelForm # Will allow for managing conflicting names easily.
form_mapping = {
'FirstModelForm': FirstModelForm,
'SecondModelForm': SecondForm,
'AdditionalAppFirstModelForm': AdditionalAppFirstModelForm,
}
request_form_class = request.POST.get('form_class')
f = form_mapping.get(request_form_class)(request.POST)
if f.is_valid():
f.save()
You're dealing with a lot of forms: Create a baseclass for your ModelForm, or replace the BaseModelFormMetaclass at runtime. You'll have to deal with issues such as name conflicts, duplicate ModelForms for the same Model "automagically", so prepare for some headaches. It would be pretty rad if you could pull it off.
Personally (as you can probably see), I'd just go with option #2.
An alternate method for this is to replace forms.py with a package called forms. Then, in __init__.py within that package, import all your ModelForms.
Then you can use sdolan's Option #1.