Dynamic class based on database query in Python (Django) - python

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 ?

Related

pass parameter from CreateView to template (Django)

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.

Django Form: "Select a valid choice. That choice is not one of the available choices."

I'm working with Python2 and Django 1.9.
Basically I have a form that contains two dropdowns. The second dropdown depends on the value of the first one.
For exampe, if dropdown #1 has the option "Category" selected, then dropdown #2 should display options "CategoryA, CategoryB and CategoryC". In the same way, if dropdown#1 has the option "Department" selected, Dropdown#2 should show "Department1, Department2, Department3".
Notice that both "Departments" and "Categories" are classes with their corresponding database tables.
So here comes my question. How to define that form? More especifcally, how do I indicate that the second form will sometimes display objects from the class Category and sometimes objects of the class Department?
This is what I have so far:
class MyClassForm(forms.Form):]
name = forms.CharField(max_length=255)
dropdown1 = forms.ModelChoiceField(
queryset=TypeOfCriteria.objects.all().order_by('name'))
dropdown2 = forms.ModelChoiceField(
queryset=Department.objects.none())
Notice how I've defined dropdodown2:
dropdown2 = forms.ModelChoiceField(
queryset=Department.objects.none())
How should I define the value of the parameter queryset for dropdown2? Since I have to specify the class that is going to be queried to obtain the list of all of its instances, how should I do it?
Right now, I'm loading the content of dropdown2 with JQuery. But when I hit the "send" button to send the post data I'm always getting the error: "Select a valid choice. That choice is not one of the available choices."
One option would be to update the queryset dynamically in the __init__ method of the form. Keep the rest of your form class as is, then add this code:
def __init__(self, *args, **kwargs):
super(MyClassForm, self).__init__(*args, **kwargs)
if 'dropdown1' in self.data:
self.fields['dropdown2'].queryset = Department.objects.filter(typeofcriteria=self.data['dropdown1'])
In the init method
def __init__(self, *arts, **kwargs):
super(MyClassForm, self).__init__(*args, **kwargs)
self.fields['dropdown2'].queryset = Department.objects.none()
if self.is_bound:
self.fields['dropdown2'].queryset = Department.objects.filter(# any value in self.data)

Django form with object list

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.

Empty ModelFormset in Django's FormWizard

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.

Django: form values not updating when model updates

I am creating a form that uses MultipleChoiceField. The values for this field are derived from another model. This method works fine, however, I am noticing (on the production server) that when I add a new item to the model in question (NoticeType), the form does not dynamically update. I have to restart the server for the new item to show up on my MultipleChoiceField.
Any changes to the NoticeType model (editing items or creating new ones) do not propagate to the form. After I restart the production server, the updates appear.
Any ideas why this might be ? The relevant portion of the form is below. Thanks.
from django import forms
from django.contrib.auth.models import User
from notification.models import NoticeType
class EditUserProfileForm(forms.Form):
CHOICES = []
for notice in NoticeType.objects.all():
CHOICES.append( (notice.label,notice.display) )
notifications = forms.MultipleChoiceField(
label="Email Notifications",
required=False,
choices=( CHOICES ),
widget=forms.CheckboxSelectMultiple,)
Although mherren is right that you can fix this problem by defining your choices in the __init__ method, there is an easier way: use the ModelMultipleChoiceField which is specifically designed to take a queryset, and updates dynamically.
class EditUserProfileForm(forms.Form):
notifications = forms. ModelMultipleChoiceField(
label="Email Notifications",
required=False,
queryset = NoticeType.objects.all(),
widget=forms.CheckboxSelectMultiple)
My hunch is that the class definition is only being processed once on load rather than for each instantiation. Try adding the CHOICES computation to the init method like so:
def __init__(self, *args, **kwargs):
super(self.__class__, self).__init__(*args, **kwargs)
CHOICES = []
for notice in NoticeType.objects.all():
CHOICES.append( (notice.label, notice.display) )
self.fields['notifications'].choices = CHOICES

Categories

Resources