I have a problem to get back an object from a django form after submission.
I have an object list (filled with MyObject, not a django model) filled by another python package.
In models.py, I have :
class MyObjectForm(forms.Form):
def __init__(self, *args, **kwargs):
# Get the list
myobjects = kwargs.pop('myobjects')
super(MyObjectForm, self).__init__(*args, **kwargs)
choices = [(o, o.name) for o in myobjects]
self.fields["my_objects"] = forms.TypedChoiceField(choices=choices)
For information, the HTML looks OK.
In views.py, form.is_valid() is always False when I click on the submit button. Is there a problem ?
When I change models.py with :
self.fields["my_objects"] = forms.TypedChoiceField(choices=choices, required=False)
In views.py, form.is_valid() is True but I can't get back my objet MyObject (I get an empty value). Is that possible ? And if yes, how can I do that ?
Look at what you used as choices... MyObject instances, really ? How is a MyObject instance supposed to be sent to a browser as part of a HTML form and then back to your server thru a POST request body ?
If you have some persistant unique identifier for each of your MyObject instances, use this for your choices, ie
choices = [(o.some_stable_and_unique_id, o.name) for o in myobjects]
Note that it won't solve all of your issues... You'll then have to subclass TypedChoiceField to retrieve the object based on its "id" etc.
Related
I've seen a similar question, alas it has not been answered.
I have an app that features Entries (like blog entries) which include a part called SubEntry. I want the users to be able to report SubEntries (i.e. press the button 'report', fill some fields and the application sends an email to admins, saving the report in db is nice to have):
The flow should be like that: at the view EntryDetails (url: /entry/entry-title/) the user may click on the SubEntry part. The modal opens and the subentry is visualized in the modal as enlarged, with a button/link underneath 'Report the SubEntry'. Then it's possible to click on the 'Report the SubEntry' button and two fields appear - reason of reporting and contact detail of the reporter (here I am just toggling the visibility of the fields). I manage to display the form (with get overriden - overriding get_form_kwargs causes the error No Entry with that title) but either the Entry or its attributes are not displayed...
My questions are:
1) is creating a model for Reporting (ReportSubEntry) a decent approach?
2) I can't seem to pass the needed variable (an Entry object that is to be a ForeignKey for a SubEntry object that is being created) from CreateReport view to the report_subentry.html.
any thoughts, advice? Python 3.5, Django 1.10
models.py:
class ReportSubentry(models.Model):
Entry = models.ForeignKey('Entry')
details = models.CharField(max_length=100)
contact = models.EmailField()
forms.py:
class ReportEntryForm(forms.ModelForm):
class Meta:
model = ReportSubEntry
fields = ['details', 'contact', 'project']
views.py:
class CreateReport(CreateView):
model = ReportSubEntry
form_class = ReportSubEntryForm
template_name = 'understand/report_subentry.html'
# tried two methods to pass the variables:
def get(self, request, *args, **kwargs):
self.object = None
title = kwargs.get('title')
kwargs['entry'] = get_object_or_404(Entry, title=title)
return super(CreateReport, self).get(request, **kwargs)
def get_form_kwargs(self, **kwargs):
title = kwargs.get('title')
kwargs['entry'] = get_object_or_404(Entry, title=title)
return kwargs
The current model that you are using ReportSubEntry is perfect and there is no need to change it.
In your forms.py ReportEntryForm you have to use relatedfields to be able to correctly serialize the data. There is no need to override anything. When user clicks on report the sub entry you have to pass the pk of Entry model as it is required to know which entry is reported. I am assuming that since you are successfully displaying the entries pk of those are present. When you receive the pk with other two fields you get the corresponding entry for pk and then pass the object to ReportSubentry.objects.create method.
The reportentry form should not contain foreign key. You have two choices for that. First is remove that field and pass the pk of entry from frontend using ajax calls or use javascript to add a disabled input field which contains pk of entry when user clicks on report subentry.
Ok, so I've solved this issue.
The only solution that worked for me was overriding the get method of the ReportSubentry without calling the get method of the superclass:
def get(self, request, *args, **kwargs):
self.object = None
title = kwargs.get('title')
entry = get_object_or_404(Entry, title=title)
context_data = self.get_context_data()
context_data.update(entry=entry)
return self.render_to_response(context_data)
Please feel free to discuss it.
According to the Django tutorial, you should access form fields using cleaned_data dictionary. I'm wondering why I can't access the properties of the form directly? My form validates just fine, but when I try to access it, Django complains that the object does not have the attribute. I added some code below that I hope will help diagnose the problem.
Form:
class CustomForm(forms.Form):
description = forms.CharField(widget = forms.TextInput(attrs = {'placeholder' : 'enter some text'}), label = "My form")
View:
def process_form(request):
if request.method != 'POST':
raise Http404
myForm = CustomForm(request.POST)
if not myForm.is_valid():
c = RequestContext(request)
return render_to_response('home/index.html', {'form' : myForm }, c)
# debug
print 'Description: ' + myForm.description # this does NOT work
# print 'Description: ' + myForm.cleaned_data['description'] # this does work
I get the following error: 'CustomForm' object has no attribute 'description'. Did I miss something in the docs that says I can't do that?
If your form is validated then you can access myForm cleaned_data:
print myForm.cleaned_data.get('description')
If you want to see why you cannot access myForm.description then you can see the data dictionary of your myForm:
print myForm.__dict__
The way you define fields using django.forms is just a convenient, declarative syntax; it's not really representative of what the final Form class, or an instance of it, looks like in terms of attributes.
Forms have a metaclass (without getting too deep into it, a metaclass is to declaring a class using the class keyword as an __init__ method is to creating an instance of a class using parentheses -- a hook to customise the object being created, which in the case of a metaclass, is a class!) which picks off Fields from the form class at definition time and adds them to a base_fields dict. When you instantiate a form, its base_fields are deep-copied to a fields attribute on the instance.
One point of confusion might be that you use . to access fields for display in templates -- what's actually happening there is that Django's template engine first attempts to use dictionary-style [] access to resolve property lookups and the base form class defines a __getitem__ method to take advantage of this, looking up the appropriate field from the form instance's fields dict and wrapping it with a BoundField, a wrapper which knows how to use the field and data from the form for displaying the field.
You can access the fields of a Form instance from its fields attribute.
myForm.fields['description']
And some property like label can be accessed like this:
myForm.fields['description'].label
Not sure how to display the value corresponding. Anybody having idea?
here is my reference
https://docs.djangoproject.com/en/dev/ref/forms/api/#accessing-the-fields-from-the-form
You can access your field trought dict.
form.__dict__["fields"]["description"]
I'd like to do something like this in Django:
class MyForm(forms.Form):
items = Items.objects.all()
for item in items:
# How does this part work?
exec(item.name) = forms.BooleanField()
The goal is to create one form field for each item returned from the database query. So, if I get ten items back from the query, then the class would have ten variables in it, each named after a returned item.
This seems theoretically possible, but is there some danger here? The items in the database are not user generated.
You can dynamically modify a form however you please:
class MyForm(forms.Form):
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
for item in Item.objects.all():
self.fields[item.name] = forms.BooleanField()
I suppose the danger is if the database state changes while a user is submitting a form, and the new form initializes with new fields which the previous form didn't have.
if I was you I would have used django formsets.
Why formsets were not an option here ?
I'm using Django's FormWizard. It works fine but I'm having trouble getting any empty model formset to display correctly.
I have a model called Domain. I'm creating a ModelFormset like this:
DomainFormset = modelformset_factory(Domain)
I pass this to the FormWizard like this:
BuyNowWizardView.as_view([DomainFormset])
I don't get any errors but when the wizard renders the page, I get a list of all Domain objects. I'd like to get an empty form. How I can do this? I've read that I can give a queryset parameter to the ModelFormset like Domain.objects.none() but it doesn't seem to work as I get errors.
Any ideas on where I'm going wrong?
Thanks
The Django docs give two ways to change the queryset for a formset.
The first way is to pass the queryset as an argument when instantiating the formset. With the formwizard, you can do this by passing instance_dict
# set the queryset for step '0' of the formset
instance_dict = {'0': Domain.objects.none()}
# in your url patterns
url(r'^$', BuyNowWizardView.as_view([UserFormSet], instance_dict=instance_dict)),
The second approach is to subclass BaseModelFormSet and override the __init__ method to use the empty queryset.
from django.forms.models import BaseModelFormSet
class BaseDomainFormSet(BaseModelFormSet):
def __init__(self, *args, **kwargs):
super(BaseDomainFormSet, self).__init__(*args, **kwargs)
self.queryset = Domain.objects.none()
DomainFormSet = modelformset_factory(Domain, formset=BaseDomainFormSet)
You then pass DomainFormSet to the form wizard as before.
I have some question:
I use django form, and fields like MultipleChoiceField
in view.py I clean data and get GET URL like this
http://localhost:8000/?category=&style=&sex=&brand=ASICS&brand=Be+Positive&low_price=&high_price=
Give me advise, can I regroup brand field and hide empty.
I want getting something like this:
http://localhost:8000/?brand=1+2
And else one question:
How can I set empty value(empty_label) for forms.ModelMultipleChoiceFIeld
forms.py:
brand = forms.MultipleChoiceField(required=False,
widget=forms.SelectMultiple(attrs={'size':1})
)
def __init__(self,app_label=None, *args, **kwargs):
super(Search, self).__init__(*args, **kwargs)
self.fields['brand'].choices = [('', 'All brands')]+[(brand.name, brand) for brand in Brand.objects.all() ]
views.py:
if request.method == 'GET' and request.GET:
form = SearchForm(app_label, request.GET)
if form.is_valid():
brands = form.cleaned_data['brand']
kwargs.update({"brand__name__in": brands})
This is how the browser submits multiple data. It's part of the HTML specification, trying to change it would be folly and technically I can't understand why you would try to care about how your url GET data looks.
That being said, if you want to change the way it submits you'll need javascript to transform the data on form submit. Django has nothing to do with the matter.
Using jQuery for example:
$('#form').submit(function(){
//Get form data
//Transform into my custom set of vars
//Redirect to form's ACTION with my querystring appended.
});
Please keep in mind you will not get any automatic parsing of the values on the Django side. Normally it would turn it into a list for you, but now you're responsible for parsing the 'value+value+value' yourself.
For empty label in forms you could do this -
class SomeForm(forms.Form):
h=forms.CharField(label=u'',widget=forms.TextInput(attrs={'value':'Search'}))
By keeping label as '', you get the label as empty. The attrs are basically the HTML attributes of the form text field.
UPDATE: I didn't understand the first part of your Q, elaborate...