How to generate a list of request.POST in Django - python

I have the following code in views.py:
def valores(request):
global peso_unitario, preco_unitario
peso_unitario=[]
preco_unitario=[]
N=a
print('N='+str(N))
for i in range(N):
peso_u = request.POST['peso_u']
preco_u = request.POST['preco_u']
if peso_u.isdigit() and preco_u.isdigit():
c = int(peso_u)
d = int(preco_u)
peso_unitario.append(c)
preco_unitario.append(d)
print(a)
if i==N-1:
return render(request, 'pacote.html', {'peso_unitario': peso_unitario, 'preco_unitario': preco_unitario})
else:
res = 'Apenas numero.'
return render(request, 'pacote.html', {'res': res})
One step before, where we filled a text field with a number N. Now, I'd like to generate N text fields to be filled by the user, but I don't know how to do this.

You should use a formset to generate N forms dynamically. You can refer to the documentation to see how to implement one.

Don't use globals. You need to store the previously retrieved number N somewhere. Maybe in the user's session. Maybe pass it to this view as a querystring, and have a default value or redirect back to the form which should have obtained it if its missing.
Anyway, having obtained that N, you can build the form with N similar forms dynamically. (As mentioned by others, another way is to use a formset).
class Baseform( forms.Form):
...
# everything apart from the variable fields
fields = {}
for n in range(N):
fields[ f'form_{n}'] = forms.Charfield( # or whatever
label = f'form{n}', ... # other form args
)
My_Dynamic_Form = type(
'My_Dynamic_Form', (BaseForm, ), fields
)
Instantiate, check as usual, process the variable fields:
form = My_Dynamic_Form( ...)
if form.is_valid():
# your variable data will be in form.cleaned_data['form_0'] upwards.
# maybe
for n in range(1000):
key = f'form_{n}'
if not (key in form.cleaned_data):
break
val = form.cleaned_data.get(key)
# do whatever with val

you can iterate all posted data using request.POST.items() example:
for key, value in request.POST.items():
print(f"{key}: {value}")
Output:
name: momo
lastname: titi

Related

Saving multiple fields of a model instance using a for loop - Django

I'm having a problem when trying to save data into an SQLite database from a Django View. Generally speaking, my approach is the following and it is intending to populate the instance inside a loop and finally save it.
1) Getting data (dict)
2) Instantiating the model
Loop - In
3) Loop over the dict to get the key, values pairs and saving them into the instance
Loop - Out
4) Saving the instance
The problem is that I'm getting an empty instance after the for loop instead of a populated one.
Now the code
models.py
class OptionsHomePage(models.Model):
option_a = models.IntegerField(choices=MINUTES_OPTIONS)
option_b = models.IntegerField(choices=HOURS_OPTIONS)
start_time_am_minutes = models.IntegerField(choices=MINUTES_OPTIONS, default=0)
start_time_am_hours = models.IntegerField(choices=HOURS_OPTIONS, default=0)
end_time_am_minutes = models.IntegerField(choices=MINUTES_OPTIONS, default=0)
end_time_am_hours = models.IntegerField(choices=HOURS_OPTIONS, default= 0)
Views Note: synonyms is a dictionary where I'm mapping the data that comes from the front to their respective values in the model
...
options = OptionsHomePage()
data_to_fill = request.GET.get('data_to_fill')
data_to_fill = json.loads(data_to_fill)
for k, v in synonyms.items():
try:
# print(data_to_fill[k])
data_name = synonyms[k]
value = data_to_fill[k]
options.data_name = value
print('{} - {} '.format(data_name,data_to_fill[k]))
except Exception as e:
print(e)
print(options)
options.save()
...
The print statements there, just for seeing what's happening there gives me the following:
end_time_pm_minutes - 10
delay_seconds - 11
screen_of_option - off
------------------------------
OptionsHomePage object (None)
------------------------------
Well... thanks in advance! If something is not well explained or shown just tell me and I can elaborate more!
It'll work:
instance = ModelInstance()
allowed_fields = ['name', 'username' .... etc ]
for field, value in data_to_fill.items():
if field in allowed_fields:
setattr(instance, field, value)
instance.save()

Django View Return Queryset with extra information

I have a normal Django view that returns the API for a query set. It takes query params from the URL and filters the database based on the parameters. It also outputs a maximum length of 3 "Part" objects.
I would like to add something so that it returns information on whether the queryset is clipped by the maximum length of 3. The idea is that since the inputs the query parameters, if the parameters are too vague, then there will be too much data being queried from the database. So it is clipped but then the user needs to know that it was clipped.
The current code looks like this
class PartList(generics.ListAPIView):
serializer_class = PartSerializer
def get_queryset(self):
"""
Optionally restricts the returned purchases to a given user,
by filtering against a `username` query parameter in the URL.
"""
queryset = Part.objects.all()
querydict = self.request.query_params
for (k, value) in querydict.items():
search_type = 'contains'
filter = k + '__' + search_type
queryset = queryset.filter(**{filter: value})
query_max_limit = 3
return queryset[:min(len(queryset), query_max_limit)]
You can try to fetch four elements, and in case it returns four, you display the first three, and specify that the data is clipped, like:
def get_queryset(self):
"""
Optionally restricts the returned purchases to a given user,
by filtering against a `username` query parameter in the URL.
"""
queryset = Part.objects.all()
querydict = self.request.query_params
for (k, value) in querydict.items():
search_type = 'contains'
filter = k + '__' + search_type
queryset = queryset.filter(**{filter: value})
query_max_limit = 3
qs = queryset[:query_max_limit+1]
self.clipped = clipped = len(qs) > query_max_limit
if clipped:
return list(qs)[:query_max_limit]
else:
return qs
So here the get_queryset will return a collection (not per se a QuerySet), containing at most three elements, and it will set an attribute self.clipped that specifies if the data was clipped.
Or a more elegant approach would be to first count, and then slice:
def get_queryset(self):
"""
Optionally restricts the returned purchases to a given user,
by filtering against a `username` query parameter in the URL.
"""
queryset = Part.objects.all()
querydict = self.request.query_params
for (k, value) in querydict.items():
search_type = 'contains'
filter = k + '__' + search_type
queryset = queryset.filter(**{filter: value})
query_max_limit = 3
qs = queryset[:query_max_limit+1]
self.clipped = clipped = qs.count() > query_max_limit
if clipped:
return queryset[:query_max_limit]
else:
return qs
It might be better to move this "clipping" logic to a dedicated function, and return if it is clipped, instead of setting an attribute.
It's perfectly fine to pass metadata along with your results, like so:
{
"is_clipped": true,
"results": [
…
]
}
Willem's answer is a good way to set is_clipped.
But I think you are interested in pagination, which is a standard way to communicate to clients that the results are clipped. It's possible combine your queryset filering with pagination. By the way, I suggest you use django-filter instead of rolling your own filtering.

too many values to unpack [Django]

def index(request):
expiring_list = probe.objects.filter("isExpired=True")
output = ', '.join([p.serial for p in expiring_list])
return HttpResponse(output)
isExpired is a Boolean function. How should I modify the filter so that the filter does not raise a ValueError?
You are making the query in a wrong format.
Your query should be of the form:
expiring_list = probe.objects.filter(isExpired = True)
This was the query you needed to make in case isExpired was your model field. But since you say its a function, assuming that function is inside the class you need to get all the objects in the following way:
expiring_list = []
objects = probe.objects.all()
for obj in objects:
if obj.isExpired() == True:
expiring_list.append(obj)
The expiring_list will now contain all the objects of the model probe where isExpired function returns True
I think isExpired is not a field in your models, as reference to your previous question Refresh a field from another table [Django]
I think exp_date is the field which you are looking for.
Try this:
import datetime
def index(request):
expiring_list = probe.objects.filter(exp_date__lt=datetime.date.today())
output = ', '.join([p.serial for p in expiring_list])
return HttpResponse(output)

iterate over django form results (not in a template)

I am trying to iterate over form results and I can't help but think that I am re-inventing the wheel here.
filterlist = []
if request.POST:
form = FilterForm(request.POST)
if form.is_valid():
for key, value in form.cleaned_data.iteritems():
filterlist.append(key)
filterlist.append(value)
This works, but seems very awkward and creates lots of other problems. For example the values come back with u' so I have to use value.encode("utf8") but then if a value is None it throws in error. So now I have to check if it is None, if not then encode. There has to be a better way.
EDIT: What I am trying to do.
I am trying to filter what is shown on a page. The problem I am running into is that if a value is empty (the user don't fill the box because they only want to filter against one object) then I get no results. For example a user wants to search for all books by the author name "Smith" but doesn't want to search against a genre.
results = Books.objects.filter(author=author, genre=genre)
The user would get no results because this is an AND search. But, if a user put in "Smith" for the author and "mystery" for the genre then it works exactly like I want it to, only giving results where both are true.
So, I am trying to eliminate the empty stuff by iterating over the form results. Like I said I am probably re-inventing the wheel here.
In Python 3 use:
for key, value in form.cleaned_data.items():
If the field names are the same in the model and the form, try this:
filter = {}
if request.method == 'POST':
form = FilterForm(request.POST)
if form.is_valid():
for key, value in form.cleaned_data.iteritems():
if value:
filter[key] = value
results = Books.objects.filter(**filter)
Python is one of the few languages having named parameters. You can assemble a dict with the non-empty form fields and pass it to the filter method using the kwargs unpacking operator **.
For example:
kwargs = {"author": "Freud"}
results = Books.objects.filter(**kwargs)
Is the same as:
results = Books.objects.filter(author="Freud")
I think the problem is that by default the Model form is not valid if a form field does not have a value entered by the user, if you don`t require the field every time from the user you need to set the required field to false in the ModelForm class in forms.py as shown in the code below. Remember that the field is set false only in the model form not in the model itself
class myForm(forms.ModelForm):
myfield_id = forms.CharField(required=False)
myfield_foo = forms.CharField(required=False)
myfield_bar = forms.CharField(required=False)
myfield_name = forms.CharField(required=False)
class Meta:
model = myModel
exclude = ('myfield_ex','myfield_file')
fields = ['myfield_id','myfield_foo','myfield_bar','myfield_name',]
After you have the form entered by the user what you need is use the Q object which can be used to create complex queries as described in the manula page here
https://docs.djangoproject.com/en/1.7/topics/db/queries/#complex-lookups-with-q
A simple example code would look like
if form.is_valid():
qgroup = []
for key,value in form.cleaned_data.iteritems():
if value:
q_name = Q(**{"%s"%format(filterKey[key]) : value})
qgroup.append(q_name)
q = None
# can use the reduce as shown here qgroup = reduce(operator.or_, (Q(**{"{0}".format(filterKey[key]): value}) for (key,value) in form.cleaned_data.iteritems()))
for key,value in form.cleaned_data.iteritems():
if value:
q_name = Q(**{"%s"%format(filterKey[key]) : value})
qgroup.append(q_name)
for x in qgroup:
q &= x ### Or use the OR operator or
if q:
resultL = myModel.objects.filter(q).select_related()
The filterKey can look something on the lines of
filterKey = {'myfield_id' : "myfield_id",
'myfield_foo' : "myfield_foo__icontains",
'myfield_bar' : "myfield_bar__relative_field__icontains",
}

change a form value before validation in Django form

I have a django form and on my view function I do this :
search_packages_form = SearchPackagesForm( data = request.POST )
I would like to overwrite a form field called price which is decleared as such :
price = forms.ChoiceField( choices = PRICE_CHOICES, required = False,widget = forms.RadioSelect )
I would like to overwrite the form field before calling search_packages_form.is_valid()
I thought of doing :
search_packages_form.data['price'] = NEW_PRICE
But it does not work. Any ideas ?
Probably not the Django way but based on https://stackoverflow.com/a/17304350/2730032 I'm guessing the easiest way to change your form value before validation is to do something like the following:
def search_packages_view(request):
if request.method == 'POST'
updated_request = request.POST.copy()
updated_request.update({'price': NEW_PRICE})
search_packages_form = SearchPackagesForm(updated_request)
if search_packages_form.is_valid():
# You're all good
This works but I'd be interested if anyone has another way that seems more in line with Django, or if there isn't: then an explanation about why.
one trick for what you want is to do it like this:
changed_data = dict(request.POST)
changed_data['price'] = NEW_PRICE
search_packages_form = SearchPackagesForm(data = changed_data)
My solution is build on an earlier proposal. It is a working solution, which can be used in several cases.
#Milad Khodabandehloo
had a tricky solution to solve the problem.
changed_data = dict(request.POST)
changed_data['price'] = NEW_PRICE
search_packages_form = SearchPackagesForm(data = changed_data)
as #The EasyLearn Academy commented: it does not allow you to access actual data submitted in form.
This is because the request.POST is immutable.
But there is a solution to the problem - just have to be more tricky.
This solution is only good if a reference to the object is enough for the certain cause. It leaves the object itself the same.
# write object to variable (data)
data = request.POST
# set to mutable
data._mutable = True
# modify the values in data
data[modified_field] = new_value
# set mutable flag back (optional)
data._mutable = False
Hope it's useful!
form.is_valid() runs through your form's (and model's in case of a ModelForm) clean method's, returning True or False
If you plan on changing form data you can do it within the general clean method or at field level, e.g.
class YourForm(DerivingClass):
# regular stuff
def clean_<ATTR>(self):
# here
return self.cleaned_data
def clean(self):
# or here
return super(YourForm, self).clean()

Categories

Resources