I'm new to python and Django and have a simple question on how to update f form that has multiple fields of same type: I have been trying to do this with formsets:
I have a simple model to store categories:
class Category(BaseModel):
categoryText = db.StringProperty()
parentCat = db.IntegerProperty()
I want to create a form that would display all available categories in input fields so they could all be edited:
using formsets to display mulitple rows of the same type:
EDIT:
figured it out:
I had to create a list of dictionary items
categories = Category.objects.all()
initialStuff = []
oneFormV={}
for cat in categories:
oneFormV.clear()
oneFormV["categoryText"]=cat.categoryText
oneFormV["parentCat"]=str(cat.parentCat)
oneFormV["catID"]=str(cat.key().id())
initialStuff.append(oneFormV.copy())
def showCategories(request):
if request.POST:
# code to update db
else:
categories = Category.objects.all()
initialStuff = []
for cat in categories:
initialStuff += "'categoryText':u'" + cat.categoryText +"'," + "'parentCat':u'" + str(cat.parentCat) +"'," + "'catID':u'" + str(cat.key().id()) + "'"
initialStuff = initialStuff [:-1] # remove last comma
CategoryFormSet = formset_factory(CategoryForm,extra=categories.count())
formset = CategoryFormSet(initial= initialStuff )
return render_to_response('adminCategories.html', {'formset': formset})
I am having problem with populating the initial data. When I generate in a loop it gives me errors :
class CategoryForm(forms.Form):
categoryText = forms.CharField()
parentCat = forms.CharField()
catID = forms.CharField()
I am assuming I need to store the ID for the fields to update them!
Finally my question:
1) Am I doing this right or is there an easier way to accomplish this?
2) my problem has been passing initial values to a formset with initial values unknown until run time.
3) should I forget about formsets and do this by adding fields to the form with init?
4) what is the correct way of initializing form fields in a formset?
AM
initialStuff must be list of dicts, not list of strings:
for cat in categories:
initialStuff.append( { categoryText: cat.categoryText,
...
}
)
So, don't remove the last comma.
If you use the operation += for list and str you get a list of strings (each str has length 1).
Please view the next: http://docs.djangoproject.com/en/dev/topics/forms/formsets/#using-initial-data-with-a-formset
Related
I have a programme where users should be able to filter different types of technologies by their attributes. My question is, how would I filter the technologies when there's potential conflicts and empty values in the parameters I use to filter?
Forms.py:
class FilterDataForm(forms.ModelForm):
ASSESSMENT = (('', ''),('Yes', 'Yes'),('No', 'No'),)
q01_suitability_for_task_x = forms.ChoiceField(label='Is the technology suitable for x?',
choices=ASSESSMENT, help_text='Please select yes or no', required=False,)
q02_suitability_for_environment_y = forms.ChoiceField(label='Is the technology suitable for environment Y?',
choices=ASSESSMENT, help_text='Please select yes or no', required=False)
There are many fields in my model like the ones above.
views.py
class TechListView(ListView):
model = MiningTech
def get_queryset(self):
q1 = self.request.GET.get('q01_suitability_for_task_x', '')
q2 = self.request.GET.get('q02_suitability_for_environment_y', '')
object_list = MiningTech.objects.filter(q01_suitability_for_task_x=q1).filter(
q02_suitability_for_environment_y=q2)
return object_list
The difficulty is that not all technology db entries will have data. So in my current setup there's times where I will filter out objects that have one attribute but not another.
For instance if my db has:
pk1: q01_suitability_for_task_x=Yes; q02_suitability_for_environment_y=Yes;
pk2: q01_suitability_for_task_x=Yes; q02_suitability_for_environment_y='';
In the form, if I don't select any value for q01_suitability_for_task_x, and select Yes for q02_suitability_for_environment_y, I get nothing back in the queryset because there are no q01_suitability_for_task_x empty fields.
Any help would be appreciated. I'm also ok with restructuring everything if need be.
The problem is that your self.request.GET.get(...) code defaults to an empty string if there is no value found, so your model .filter() is looking for matches where the string is ''.
I would restructure the first part of get_queryset() to build a dictionary that can be unpacked into your filter. If the value doesn't exist then it doesn't get added to the filter dictionary:
filters = {}
q1 = self.request.GET.get('q01_suitability_for_task_x', None)
q2 = self.request.GET.get('q02_suitability_for_environment_y', None)
if q1 is not None:
filters['q01_suitability_for_task_x'] = q1
... etc ...
object_list = MiningTech.objects.filter(**filters)
If you have a lot of q1, q2, etc. items then consider putting them in a list, looping through and inserting into the dictionary if .get(...) returns anything.
Edit: Because there are indeed a lot possible filters, the final solution looks as follows:
def get_queryset(self):
filters = {}
for key, value in self.request.GET.items():
if value != '':
filters[key] = value
object_list = Tech.objects.filter(**filters)
i'm running into issues with the following, and I'm wondering if it is even possible.
I have a flask-admin adminview setup, with an extra form field which shows a dropdown based on a specific column (category) in the sql model. See code for clarification:
model:
class Item(db.Model):
id = db.Column(db.Integer, primary_key = True)
name = db.Column(db.String(128), index = True)
category = db.Column(db.String(16))
I have the extra form field in Flask-Admin as follows:
form_extra_fields = {
'category': QuerySelectField(
label='Categories',
query_factory = lambda: db.session.query(Item),
get_label = 'category',
)
}
This all works fine except if there are duplicates in the category column, then the dropdown is populated with these duplicate values. Is it possible to remove those duplicates from dropdown, or at least only show the unique values?
Basically I solved this by overriding a class method in QuerySelectField class as follows, by appending unique labels to a list and check if every next label is in that list. I'm still thinking there should be a better way though...
def iter_choices(self):
labels = []
if self.allow_blank:
yield ('__None', self.blank_text, self.data is None)
for pk, obj in self._get_object_list():
if self.get_label(obj) not in labels:
labels.append(self.get_label(obj))
yield (pk, self.get_label(obj), obj == self.data)
Ok im new to django
So ive got a situation where i want a formset to have dynamic initial data
So basically here is what im looking for.
each form in the formset to have a different UserID
and a set of groups permission which they can choose from based from the initial data
here is my form
class assignGroupPermissionToUser(forms.ModelForm):
UserID = forms.ModelChoiceField(queryset=None)
Groups = forms.ModelMultipleCHoiceField(queryset=None, widget=FilteredSelectMultiple("Groups")
class Meta:
model=User
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
Userid = kwargs.pop("UserID")
self.fields['UserID'].queryset =User.objects.get(UserID=Userid)
Permissions = kwargs.pop("Groups")
listofPermission = None
for each perm in permission:
listofPermission |= Permissions.objects.filter(GroupID=perm)
self.fields['Groups'].queryset = listofPermission
the data i wanna pass is built into a list like so
it is called
completeList
> completeList =[['13452',{'group1':'Admin','group2':'FrontDesk'}],['3532','group1':'Supervisors','group2':'ReadOnly;}]]
where the first value in each nested loop is the UserID, and the dictionary is the groups they can choose from.
override method in View.py
....
form = assignGroupPermissionToUser()
assignment = formset_factory(form,extra=0)
formset = [ assignment.__init__(completeList[x][0],completeList[x][1]) for x in range(len(completeList))]
then i get an error that str object has no 'is_bound' field line 58 of formset.py
im trytin to get this data to show up on each form and based on the user
it will be all different but everything i try to override it fails for initial form so here i am stuck
note that the Group attribute in the modelform has a widget which is used in the admin section to filter from multiple choices.
settings
Django= 1.8
python 3.5
i erased all this code and just did two loops like so
formset = assignments(initial=[{'UserID': listofUserID[x] } for x in range(len(completeList))])
#then
for form in formset:
form.fields['permissions'].queryset = querysetiwant
Say I have django model that looks something like this:
class Order(models.Model):
number = models...
date = models...
class OrderLine(models.Model):
# One or more lines per order
order = models.ForeginKey(Order)
common_line = models.OneToOneField(CommonLine)
class CommonLine(models.Model):
# common elements of what might be on a line item...
taxes = model...
amount = model...
I want to create a form that uses an inlineformset to edit one or more Lines (both OrderLine and CommonLine) per order.
I can create a formset that works with Order and OrderLine - but how do I get the inline formset to give me all the detailed items from the CommonLine class when displaying the formset. It seems the documentation on inline formsets requires that the inline form - the multiple lines on an order can only map to a single class...
Am I not seeing something in the documentation? I'm sure I can probably override something, I'm just not sure where.
Thanks for any help...
I solved problem with http://yergler.net/blog/2009/09/27/nested-formsets-with-django/. Pleas use the following correction in forms.py file:
instance=None
pk_value = hash(form.prefix)
+ correct_data = None
+ if (self.data):
+ correct_data = self.data;
# store the formset in the .nested property
form.nested = [
- TenantFormset(data=self.data,
+ TenantFormset(data=correct_data,
instance = instance,
Just working on Django 1.4.1 very well.
Some minor changes were needed to make Nathan's code at http://yergler.net/blog/2009/09/27/nested-formsets-with-django/ work in Django 1.3. The line below causes a ManagementForm Error.
TenantFormset = inlineformset_factory(models.Building, models.Tenant, extra=1)
Usings the modelformset_factory and manually defining the queryset seems to work, but I have not implemented the ability to add extras.
TenantFormset = modelformset_factory(models.Tenant, extra=0)
form.nested = [
TenantFormset(
queryset = Tenant.objects.filter(building = pk_value),
prefix = 'value_%s' % pk_value
)
]
I also had to manually pass data to the sub-sub-forms in the is_valid method:
def is_valid(self):
result = super(BaseProtocolEventFormSet, self).is_valid()
for form in self.forms:
if hasattr(form, 'nested'):
for n in form.nested:
n.data = form.data
if form.is_bound:
n.is_bound = True
for nform in n:
nform.data = form.data
if form.is_bound:
nform.is_bound = True
# make sure each nested formset is valid as well
result = result and n.is_valid()
return result
EDIT:
New instances can be created using jQuery. See this question:
This sounds very similar to the approach talked about at help http://yergler.net/blog/2009/09/27/nested-formsets-with-django/ where Nathan writes about how he catered for "a multi-level data model; an example of this kind of model would be modeling City Blocks, where each Block has one or more Buildings, and each Building has one or more Tenants."
Some more explanations can aslo be found here Django Forms Newbie Question
class Person(models.Model):
name = models.CharField(max_length=100)
class Entry(models.Model):
text = models.CharField(max_length=100)
person = models.ManyToManyField(Person, blank=True, null=True)
class MyModelForm(forms.ModelForm):
class Meta:
model = Entry
In my view, I need to add pk id's to a submitted form before saving it.
data = request.POST.copy()
# 'person' is a ManyToManyField to a 'Person' model
# a form would normally send multiple id's as POST in this format -> u'id': [u'1', u'2']
# u'1,2' (an example) is a str variable accessible to the view
data[u'person'] = u'1,2'.split(",")
form = MyModelForm(data)
if form.is_valid():
form.save()
This gives me:
int() argument must be a string or a
number, not 'list'
Which is fair enough. It does work in case of:
data[u'person'] = u'1'
I also tried this with no success:
new_form = form.save(commit=False)
new_form.person = u'1,2'.split(",")
new_form.save()
form.save_m2m()
How can I save multiple id's to this ManyToManyField?
Must be easy but I am missing the point.
EDIT:
The desired result is one new instance of MyModelForm (in the 'entry' table) with all id's stored for form.person (multiple records in the 'entry_person' table).
UPDATE:
I seem to have isolated the problem.
If I do:
data = {}
data[u'person'] = u'1,2'.split(",")
It does work as the result is:
{u'person': [u'1', u'2'],}
If I do:
data = request.POST.copy()
data[u'person'] = u'1,2'.split(",")
It does NOT work (gives the error described above) as the result is:
<QueryDict: {u'person': [[u'1', u'2']],}>
So all I need is to have
<QueryDict: {u'person': [u'1', u'2'],}>
Any suggestions how?
QueryDict.setlist(key, list_) solves this problem.
Answered in A workaround for Django QueryDict wrapping values in lists?
The split you entered returns the following:
[u'1', u'2']
To create multiple instances in your database you'd need to iterate over the list:
for x in u'1,2'.split(","):
data[u'person'] = x
form = MyModelForm(data)
if form.is_valid():
form.save()
Out of curiosity, why string to list conversion in the first place? Or is this just a general example
Try:
new_form.person = [int(x) for x in u'1,2'.split(",")]
This will set new_form.person to a list of ints, not a list of strings.