I need a form that has a dynamically created part and a static part to it. So I'm thinking of subclassing ... but can't figure out how this would work with a dynamically created form.
I am creating my dynamic form in this way:
from views.py
def create_legumes_form_class(legumes):
form_fields={}
for legume in legumes:
field_id = 's_{}'.format(legume.id)
form_fields[field_id] = IntegerField(default=0 , validators = [InputRequired()])
return type('OrderForm', (Form,), form_fields)
Then instantiating the form in my view function :
legumes = Legumes.query.filter_by(disponible = True).all()
OrderForm = create_legumes_form_class(legumes)
form = OrderForm()
But to this OrderForm(), I need to add a couple BooleanField(s) that will always be the same (ie. not dynamically created from the Query).
So I am trying to add a (static) form in my forms.py and instantiate it like this in the view function:
form=OrderForm(StaticForm)
But I'm getting
TypeError: formdata should be a multidict-type wrapper that supports the 'getlist' method
And I don't know where to take it from here !
What am I doing wrong/not doing ?
You can't pass another class to the OrderForm constructor and have it subclass that class. You can, however, provide it when you call type.
return type('OrderForm', (Form, StaticForm), form_fields)
If StaticForm subclasses Form this can be further simplified.
return type('OrderForm', (StaticForm,), form_fields)
Related
I am trying to use a Django UpdateView to display an update form for the user. https://docs.djangoproject.com/en/1.8/ref/class-based-views/generic-editing/
I only want the user to be able to edit their own form.
How can I filter or restrict the the objects in the model to only show objects belonging to the authenticated user?
When the user only has one object I can use this:
def get_object(self, queryset=None):
return self.request.user.profile.researcher
However, I now need the user to be able to edit multiple objects.
UPDATE:
class ExperimentList(ListView):
model = Experiment
template_name = 'part_finder/experiment_list.html'
def get_queryset(self):
self.researcher = get_object_or_404(Researcher, id=self.args[0])
return Experiment.objects.filter(researcher=self.researcher)
class ExperimentUpdate(UpdateView):
model = Experiment
template_name = 'part_finder/experiment_update.html'
success_url='/part_finder/'
fields = ['name','short_description','long_description','duration', 'city','address', 'url']
def get_queryset(self):
qs = super(ExperimentUpdate, self).get_queryset()
return qs.filter(researcher=self.request.user.profile.researcher)
URL:
url(r'^experiment/update/(?P<pk>[\w\-]+)/$', login_required(ExperimentUpdate.as_view()), name='update_experiment'),
UpdateView is only for one object; you'd need to implement a ListView that is filtered for objects belonging to that user, and then provide edit links appropriately.
To prevent someone from simply putting the URL for an edit view explicitly, you can override get_object (as you are doing in your question) and return an appropriate response.
I have successfully been able to generate the list view and can get
the update view to work by passing a PK. However, when trying to
override the UpdateView get_object, I'm still running into problems.
Simply override the get_queryset method:
def get_queryset(self):
qs = super(ExperimentUpdate, self).get_queryset()
# replace this with whatever makes sense for your application
return qs.filter(user=self.request.user)
If you do the above, then you don't need to override get_object.
The other (more complicated) option is to use custom form classes in your UpdateView; one for each of the objects - or simply use a normal method-based-view with multiple objects.
As the previous answer has indicated, act on the list to show only the elements belonging to the user.
Then in the update view you can limit the queryset which is used to pick the object by overriding
def get_queryset(self):
qs = super(YourUpdateView, self).get_queryset()
return qs.filter(user=self.request.user)
I'm trying to write an app for Django. I want my users to be able to collect certain types of data, for instance samples, videos, etc... The app is called collector and for each type of item there is a class and a form that goes along with it.
Example Class:
class CreateTextView(CreateItemView):
form_class = TextForm
model = Text
def get_context_data(self, **kwargs):
context = super(CreateTextView, self).get_context_data(**kwargs)
context['item_type'] = 'text'
return context
Example Form:
class TextForm(forms.ModelForm):
class Meta:
model = Text
fields = COMMON_FIELDS + ('text',)
As you can see, the actual view is inheriting from CreateItemView. I want as much of the functionality to be defined for CreateItemView so that I don't have to do it individually for all item classes. That has been working for the most part, but it gets a bit tricky when I try to process forms with data.
def post(self, request, *args, **kwargs):
form = TextForm(request.POST) # line 2
form = getattr(TextForm, '__init__')(data=request.POST) # line 3
if form.is_valid():
# Add owner information.
item = form.save(commit=False)
item.owner = request.user
item.save()
return HttpResponseRedirect(reverse('collector:index'))
return render(request, self.template_name, {'form': form})
In Line 2 you can see how I would handle the form if there was only one type of form. Line 3 is what I'm trying to do. I want to be able to use the context['item_type'] to dynamically choose the right form and instantiate it with the given data.
Now the problem lies with the __init__-method which I have never defined anywhere. When I pass only POST.request to __init__, it complains about not having a self. When I pass the additional self, it complains about how CreateTextView has no _meta-attribute and so on. I just can't find the right combination of argumentes to satisfy the __init__-method. I can't look up it's definition either, because I didn't define it. I then followed the definition of the parent classes in the django framework which led me to a couple of complex functions that looked like factories. That didn't really help me...
Now I know how to use the TextForm()-initiation. Is there a way to fetch this method dynamically with getattr()? That would save me the hassle with __init__. If not, how do I provide __init__ with the correct self-instance?
As mentioned below, I have changed my classes a little bit. I no longer use context to store the item_type, instead I use a class variable to have easy acces to the item_type within a view. My post method is defined in the mother class CreateItemView and looks like this now:
def post(self, request, *args, **kwargs):
try:
form_cls = ITEM_TYPE_MAP[self.item_type]
except KeyError:
# invalid item_type. raise a meaningful error here
raise Exception('Invalid item type.')
form = form_cls(request.POST)
if form.is_valid():
# Add owner information.
item = form.save(commit=False)
item.owner = request.user
item.save()
return HttpResponseRedirect(reverse('collector:index'))
return render(request, self.template_name, {'form': form})
A clean and quite simple solution to look for is using a dictionary to map the item_type values to actual form classes:
ITEM_TYPE_MAP = {
"foo": TextForm,
"bar": SomeOtherForm,
}
You’d put that dictionary at some global place and use it from within the controller like this:
item_type = context['item_type']
try:
form_cls = ITEM_TYPE_MAP[item_type]
except KeyError:
# invalid item_type. raise a meaningful error here
raise
form = form_cls(request.POST)
You cannot directly call __init__ usually, because there’s more than that to instanciate an object. Python will also call __new__ on the class of the object, so the only way to be sure is to go through the actual constructor, which is calling the type.
This is what happens above, by first fetching the type into form_cls and then calling the type (i.e. the constructor).
Basically what I'm trying to achieve is a multi-model django app where different models take advantage of the same views. For example I've got the models 'Car' 'Make' 'Model' etc and I want to build a single view to perform the same task for each, such as add, delete and edit, so I don't have to create a seperate view for add car, ass make etc. I've built a ModelForm and Model object for each and would want to create a blank object when adding and a pre-populated form object when editing (through the form instance arg), with objects being determined via url parameters.
Where I'm stuck is that I'm not sure what the best way to so this is. At the moment I'm using a load of if statements to return the desired object or form based on parameters I'm giving it, which get's a bit tricky when different forms need specifying and whether they need an instance or not. Although this seems to be far from the most efficient way of achieving this.
Django seems to have functions to cover most repetitive tasks, is there some magic I'm missing here?
edit - Here's an example of what I'm doing with the arguments I'm passing into the url:
def edit_object(request, object, id):
if(object==car):
form = carForm(instance = Car.objects.get(pk=id)
return render(request, 'template.html', {'form':form})
What about using Class Based Views? Using CBVs is the best way in Django to make reusable code. For this example maybe it can be a little longer than function based views, but when the project grows up it makes the difference. Also remember "Explicit is better than implicit".
urls.py
# Edit
url(r'^car/edit/(?P<pk>\d+)/$', EditCar.as_view(), name='edit-car'),
url(r'^make/edit/(?P<pk>\d+)/$', EditMake.as_view(), name='edit-make'),
# Delete
url(r'^car/delete/(?P<pk>\d+)/$', DeleteCar.as_view(), name='delete-car'),
url(r'^make/delete/(?P<pk>\d+)/$', DeleteMake.as_view(), name='delete-make'),
views.py
class EditSomethingMixin(object):
"""Use Mixins to reuse common behavior"""
template_name = 'template-edit.html'
class EditCar(EditSomethingMixin, UpdateView):
model = Car
form_class = CarForm
class EditMake(EditSomethingMixin, UpdateView):
model = Make
form_class = MakeForm
class DeleteSomethingMixin(object):
"""Use Mixins to reuse common behavior"""
template_name = 'template-delete.html'
class DeleteCar(DeleteSomethingMixin, DeleteView):
model = Car
class DeleteMake(DeleteSomethingMixin, DeleteView):
model = Make
Just pass your class and form as args to the method then call them in the code.
def edit_object(request, model_cls, model_form, id):
form = model_form(instance = model_cls.objects.get(pk=id)
return render(request, 'template.html', {'form':form})
then just pass in the correct classes and forms in your view methods
def edit_car(request,id):
return edit_object(request, Car, CarForm, id)
each method knows what classes to pass, so you eliminate the if statements.
urls.py
url(r'^car/delete/(?<pk>\d+)/', edit, {'model': Car})
url(r'^make/delete/(?<pk>\d+)/', edit, {'model': Make})
views.py
def edit(request, id, model):
model.objects.get(id=id).delete()
I have a wtforms form
class MyForm(Form):
names = SelectField('name', choices=[])
The choices of names field is built dynamically and rendered in the template by an ajax call. When I submit the form, it raises an exception "not a valid choice". I don't want the form to validate the choices of names field for me. How can I disable the validation?
I did something like this to step around the SelectMultipleField validation in WTForms. It should work the same way with a plain SelectField
class NonValidatingSelectMultipleField(SelectMultipleField):
"""
Attempt to make an open ended select multiple field that can accept dynamic
choices added by the browser.
"""
def pre_validate(self, form):
pass
I simply override the built-in validation.
I was stuck with the same issue. The solution provided by Xealot is great. I found that there is an option to set validation to False using validate_choice=False. I have included an example of both the solutions below.
class NonValidatingSelectField(SelectField):
"""
Attempt to make an open ended select multiple field that can accept dynamic
choices added by the browser.
"""
def pre_validate(self, form):
pass
class MyForm(Form):
names = NonValidatingSelectField('name')
names2 = SelectField('name2', validate_choice=False)
By "I don't want the form to validate the choices", I assume you actually mean "I'm going to do it myself later and doubt the form's ability to do it correctly".
But you are in luck! You can subclass an existing form to add choices dynamically.
class MyForm(Form):
# other fields...
def some_handler(request):
name_choices = build_name_choices()
class RealForm(MyForm):
names = SelectField('name', choices=name_choices)
form = RealForm(request.GET)
form.validate()
This also saves you from the tedium of merging the form's validation and error messages with those you generate yourself later.
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"]