Django form search - python

I am making a search form in django, and I am struggling with processing the form in my view.
My code:
class SearchForm(forms.Form):
name = forms.CharField(label="Name", max_length=64, required=False)
...
<a few other fields>
def search(request):
if request.method == 'POST':
form = SearchForm(request.POST)
if form.is_valid():
name = form.cleaned_data['name']
qdict = { 'name': name}
q_objs = [Q(**{qdict[k]: form.cleaned_data[k]}) for k in qdict.keys() if form.cleaned_data.get(k, None)]
search_results = Group.objects.select_related().filter(*q_objs)
response = {'success' : search_results}
return HttpResponse(simplejson.dumps(response, ensure_ascii=False), mimetype='application/javascript')
else:
form = SearchForm()
return render_to_response("main/search.html", {'form': form},
context_instance=RequestContext(request))
I get this error:
Cannot resolve keyword u'NAME' into field. Choices are: date_submitted, id, name, parameters.
I realized that this field is in unicode and tried converting it with str(...) or with encode('ascii',...), but it still gives me the same error. I am new to django, so any help would be appreciated.
Thanks

To find your error code replace this code:
name = form.cleaned_data['name']
qdict = { 'name': name}
q_objs = [Q(**{qdict[k]: form.cleaned_data[k]}) for k in qdict.keys() if form.cleaned_data.get(k, None)]
search_results = Group.objects.select_related().filter(*q_objs)
By this one:
q=None
for k,v in form.cleaned_data.items():
if q:
q &= Q( k = v )
else:
q = Q( k = v )
search_results = Group.objects.select_related().filter( q )
But, to have really control over your query, you need to write condition by condition:
qs = []
name = form.cleaned_data['name']
if name:
q_name = Q( name__contains = name )
qs.append(q_name)
fromDate = form.cleaned_data['fromDate']
if fromDate:
q_from = Q( date__gte = fromDate )
qs.append(q_from)
toDate = form.cleaned_data['toDate']
if toDate:
q_toDate = Q( date__gte = toDate )
qs.append(q_toDate)
q=None
for x in qs:
if q:
q &= x
else:
q = x
search_results = Group.objects.select_related().filter(q)

It complains about the (uppercase) NAME field, and judging by the error message's format, it is the query that triggers it. I can't really tell from your code, but at some point I think you execute the equivalent of the following:
Group.objects.filter(NAME='some_value')
If the Group model (which you didn't post, so this is an educated guess) contains a lowercase name field, the above query will generate the error you posted as it tries to access the uppercase NAME field.
So I guess it boils down to: what does the final query look like? For getting information on that, danihp's comment already provided a good breakdown for how to determine this.

Related

I want to get only customer id but getting whole customer information

I am trying to show orders by customer id by i am getting this error :
TypeError at /orders
Field 'id' expected a number but got {'id': 3, 'phone_number': '01622153196', 'email': 'sakibovi#gmail.com', 'password': 'pbkdf2_sha256$216000$H2o5Do81kxI0$2tmMwSnSJHBVBTU9tQ8/tkN7h1ZQpRKrTAKkax1xp2Y=', 'coin': 1200.0}.
Actually i want to fetc only customer id but getting whole dictionary.
Here in Login Class in views.py i fetch whole customers info like this
request.session['customer'] = customer.__dict__
Here is the details :
class Login(View):
def get(self, request):
return render(request, 'signupsignin/signin.html')
def post(self, request):
phone_number = request.POST.get('phone_number')
password = request.POST.get('password')
customer = Customer.get_customer(phone_number)
error_message = None
if customer:
match = check_password(password, customer.password)
if match:
customer.__dict__.pop('_state')
request.session['customer'] = customer.__dict__
# request.session['customer'] = customer.id
#request.session['customer'] = customer.coin
#request.session['phone_number'] = customer.phone_number
return redirect('Home')
else:
error_message = 'Phone number or Password didnt match on our record'
else:
error_message = 'No Customer Found! Please Registrer First!'
print(phone_number, password)
context = {'error_message':error_message}
return render(request, 'signupsignin/signin.html', context)
I think for that reason i am getting the whole informaton of a customer
Here is my Views.py for userorders by customer id ::
class UserOrders(View):
def get(self, request):
customer = request.session.get('customer')
user_orders = Order.get_orders_by_customer(customer)
print(user_orders)
args = {'user_orders':user_orders}
return render(self.request, 'Home/all_orders.html', args)
Here i have a method named get_orders_by_customer() i made this in models.py
Here it is ::
#staticmethod
def get_orders_by_customer(customer__id):
return Order.objects.filter(customer=customer__id)
So what i am trying to do is customers can see their own orders.I have a panel called "all orders" here a customer can see their own order only.
Please Help me i got really confused here
As per my understanding, you have to pass a number, but you're passing a whole dictionary.
#staticmethod
def get_orders_by_customer(customer__id):
return Order.objects.filter(customer=customer__id)
here before return try to debug with a print or something. and you'll see what I mean.
try this and it should work, if im not wrong:
costomer__id['id'] instead of costomer__id
or change your code into this:
#staticmethod
def get_orders_by_customer(customer):
return Order.objects.filter(customer=customer['id'])
You can try using values() query to achieve your purpose.
Here is the link to the documentation - https://docs.djangoproject.com/en/3.1/ref/models/querysets/#values

Django form field update

I have a Django form that lets users choose a tag from multiple tag options. The problem I am facing is that even when the tag list gets updated, the model form does not get the updated tag list from database. As a result, new tags do not appear in options.
Here is my code in forms.py:
class EnglishTagForm(forms.Form):
tag_choices = [(x.tagName, x.tagName.upper()) for x in ClassTag.objects.filter(
agentId=Agent.objects.get(name='English Chowdhury'))]
tag = forms.CharField(widget=forms.Select(choices=tag_choices,
attrs={'class':'form-control'}))
def __init__(self, *args, **kwargs):
super(EnglishTagForm, self).__init__(*args, **kwargs)
self.fields['tag'].choices = [(x.tagName,
x.tagName.upper()) for x in ClassTag.objects.filter(
agentId=Agent.objects.get(name='English Chowdhury'))]
This form is being instantiated in view. My question is what changes should I do so that tag_choices gets updated from database on every instantiation.
How the above form is used in views.py:
```
def complaintDetail(request, complaint_id):
complaint = Complaints.objects.filter(pk=complaint_id).first()
context = {}
if request.method == 'POST':
agent = Agent.objects.get(name="English Chowdhury")
if "SubmitTag" in request.POST:
englishForm = EnglishTagForm(request.POST)
if englishForm.is_valid:
// Complaint Delete Logic
return redirect('chatbot:modComplaints')
else:
englishForm = EnglishTagForm()
context['eForm'] = englishForm
elif "SubmitBundle" in request.POST:
newTagForm = NewTagForm(request.POST)
if newTagForm.is_valid():
// Complaint Delete Logic
complaint.delete()
return redirect('chatbot:modComplaints')
else:
newTagForm = NewTagForm()
context['newForm'] = newTagForm
else:
englishForm = EnglishTagForm()
context['eForm'] = englishForm
newTagForm = NewTagForm()
context['newForm'] = newTagForm
context['complaint'] = complaint
return render(request, 'chatbot/complaintDetail.html', context)
```
Edit: (For future reference)
I decided to modify the tag attribute and convert CharField to ModelChoiceField, which seems to fix the issue.
Updated Class:
class EnglishTagForm(forms.Form):
tag = forms.ModelChoiceField(queryset=ClassTag.objects.filter(
agentId=Agent.objects.get(name='English Chowdhury')),
empty_label=None, widget=forms.Select(
attrs={'class':'form-control'}))
Please remove the list comprehension from Line 2. So that,
tag_choices = [(x.tagName, x.tagName.upper()) for x in ClassTag.objects.filter(
agentId=Agent.objects.get(name='English Chowdhury'))]
becomes
tag_choices = []

How to work with context in Django

I'm trying to create a confirmation page. User can create orders on one page, then the create_order view validates the forms and send a request with context to another view which is called confirm_order. I think that I would work correct but there is one problem. The first time confirm_order gets request and context which contains data from forms. But when User clicks on confirm in this page, the confirm_view is called without this context so I'm getting error:
> ValidationError at /create-job/ [u'ManagementForm data is missing or
> has been tampered with']
Do you guys know how to send the context second time?
Here are those two views:
def create_order(request):
LanguageLevelFormSet = formset_factory(LanguageLevelForm, extra=5, max_num=5)
language_level_formset = LanguageLevelFormSet(request.POST or None)
job_creation_form = JobCreationForm(request.POST or None, request.FILES or None)
context = {'job_creation_form': job_creation_form,
'formset': language_level_formset}
if request.method == 'POST':
if job_creation_form.is_valid() and language_level_formset.is_valid():
cleaned_data_job_creation_form = job_creation_form.cleaned_data
cleaned_data_language_level_formset = language_level_formset.cleaned_data
context = {
'cleaned_data_job_creation_form': cleaned_data_job_creation_form,
"cleaned_data_language_level_formset": cleaned_data_language_level_formset,
}
mutable = request.POST._mutable # I'm adding parameter 'review' to be able to differ between two different posts in confirm_order view
request.POST._mutable = True
request.POST['review'] = True
request.POST._mutable = mutable
return confirm_order(request, context)
else:
return render(request, 'auth/jobs/create-job.html', context=context)
return render(request, 'auth/jobs/create-job.html', context=context)
def confirm_order(request, context):
print context
cleaned_data_job_creation_form = context['cleaned_data_job_creation_form']
cleaned_data_language_level_formset = context['cleaned_data_language_level_formset']
print request.POST['review']
if request.method == 'POST' and request.POST['review'] == True:
file = cleaned_data_job_creation_form['file']
count = 5 #simplified multiple rows
jobs = []
for language_level_form in [d for d in cleaned_data_language_level_formset if d]:
language = language_level_form['language']
level = language_level_form['level']
d = {}
d['language_from'] = cleaned_data_job_creation_form['language_from'].name
d['language_to'] = language
d['number_of_characters'] = count
d['price_per_sign'] = 1
d['estimated_price'] = count * d['price_per_sign']
jobs.append(d)
table = CreatedOrdersTable(jobs)
context = {'table': table,
'cleaned_data_job_creation_form': cleaned_data_job_creation_form,
'cleaned_data_language_level_formset': cleaned_data_language_level_formset}
return render(request, 'auth/jobs/confirm-order.html', context=context)
else:
for language_level_form in [d for d in cleaned_data_language_level_formset if d]:
language = language_level_form['language']
level = language_level_form['level']
Job.objects.create(
customer=request.user,
text_to_translate=cleaned_data_job_creation_form['text_to_translate'],
file=cleaned_data_job_creation_form['file'],
short_description=cleaned_data_job_creation_form['short_description'],
notes=cleaned_data_job_creation_form['notes'],
language_from=cleaned_data_job_creation_form['language_from'],
language_to=language,
level=level,
)
return HttpResponseRedirect('/order-success')
You are returning a template render on your first view. So, the request doesn't end on your confirm_order view.
If you want, you can redirect to reverse('confirm_order') and add the data on session.
In your confirm_order view you will pop the data from session and continue with your flow.

Filtering data with __range

I'm using django_tables2 thus part of the code is dependent on this package, but this should be irrelevant to the overall problem.
forms.py
class PersonForm(forms.Form):
date_from = forms.DateField(input_formats='%d/%m/%Y')
date_to = forms.DateField(input_formats='%d/%m/%Y')
views.py
def filter(request):
table = PersonTable(Person.objects.all())
if request.method == 'POST':
form = PersonForm(request.POST)
if form.is_valid():
date_from = form.cleaned_data['date_from']
date_to = form.cleaned_data['date_to']
result_filtered = table.filter(date__range=(date_from, date_to))
RequestConfig(request, paginate={"per_page": 100}).configure(table)
return render(request, "people.html", {
"table": result_filtered })
args = {}
args.update(csrf(request))
args['form'] = PersonForm()
args['table'] = table
RequestConfig(request, paginate={"per_page": 100}).configure(table)
return render(request, 'people.html', args)
Simply, filtering is not working. I can see the entire table, but when I try to filter nothing happens. Can you see what's wrong?
I'm pretty sure you need to call .filter() on the query set rather than the table. For example:
result_filtered = PersonTable(Person.objects.filter(date__range=(date_from, date_to))
Also, on this line:
RequestConfig(request, paginate={"per_page": 100}).configure(table)
You are passing in table. You should pass in result_filtered instead.
This is one way I'd do it, assuming your Person model has a date field:
def filter(request):
if 'date_from' in request.GET and 'date_to' in request.GET:
# form data has been submitted
form = PersonForm(request.GET)
if form.is_valid():
date_from = form.cleaned_data['date_from']
date_to = form.cleaned_data['date_to']
people = Person.objects.filter(date__range=(date_from, date_to))
table = PersonTable(people)
else:
table = PersonTable(Person.objects.all())
else:
form = PersonForm()
table = PersonTable(Person.objects.all())
RequestConfig(request, paginate={"per_page": 100}).configure(table)
args = {}
args.update(csrf(request))
args['form'] = form
args['table'] = table
return render(request, 'people.html', args)
This binds the form if both its expected fields are present, and limits the queryset according to the results if valid. If not valid, it renders the bound form and the table built from the unlimited table. If form data was not submitted, it renders the table built from the unlimited table.
Your form tag's method attribute should be GET rather than POST, if using this design.
This doesn't quite follow the usual Django patterns for form handling because the form isn't actually making any changes, so you can use GET and don't need to return a redirect on success.

Decimal value in form wont validate Django

Here's my form i am trying to user to pass two char fields and two coordinates so they can be turned into objects. I just figured out the validation on the char fields, but now the decimal fields wont work. Any suggestions?
from django import forms
class SubmitForm(forms.Form):
title = forms.CharField(max_length=100)
story = forms.CharField(max_length=3000)
lat = forms.DecimalField(max_digits=25, decimal_places=20)
lng = forms.DecimalField(max_digits=25, decimal_places=20)
def clean_title(self):
if len(self.cleaned_data['title']) < 4:
raise forms.ValidationError("Enter your full title")
# Always return the cleaned data
return self.cleaned_data['title']
def clean_story(self):
if len(self.cleaned_data['story']) < 4:
raise forms.ValidationError("Enter your full story")
# Always return the cleaned data
return self.cleaned_data['story']
def clean_lng(self):
if self.cleaned_data['lng'] == 0.0:
raise forms.ValidationError("Enter your full story")
return self.cleaned_data['lng']
def clean_lat(self):
if self.cleaned_data['lat'] == 0.0:
raise forms.ValidationError("Enter your full story")
# Always return the cleaned data
return self.cleaned_data['lat']
def clean(self):
cleaned_data = self.cleaned_data
return cleaned_data
Here's my view
def test(request):
ctxt = {}
if request.method == 'POST':
form = SubmitForm(request.POST) # A form bound to the POST data
if form.is_valid():
lat1 = form.cleaned_data['lat']
lng1 = form.cleaned_data['lng']
# title1 = form.cleaned_data['title']
titlepost = form.cleaned_data['title']
story1 = form.cleaned_data['story']
ctxt = {'titlehere':titlepost}
catid = "test1234"
cat = Category(category=catid)
cat.full_clean()
cat.save()
marker = Marker(lat=lat1, lng=lng1,category=cat, title=titlepost, story=story1)
marker.full_clean()
marker.save()
return render_to_response('home.html', ctxt, context_instance=RequestContext(request))
else:
return render_to_response('test.html', ctxt, context_instance=RequestContext(request))
else:
now = datetime.datetime.now()
form = SubmitForm()
latest_marks = Marker.objects.all().order_by('-submitted')[0:10]
ctxt = {
'marks':latest_marks,
'now':now.date(),
'form': form,
}
return render_to_response('test.html', ctxt, context_instance=RequestContext(request))
For some reason each time i send the form I get this error message
Not really sure what going on. Still pretty new to Django. Any help would be appreciated.
Edit: add in colons I was missing. Error still consists
You're missing some colons after your if statements in the clean methods of your form.
Did you consider using Modelforms ?
You can simplify your code just by using a modelform for Marker model (less code duplication, less code in general to create the model ...)
FYI:
You can send the min_length to CharField and get the len() validation for free :)
class SubmitForm(forms.Form):
title = forms.CharField(max_length=100, min_length=4)
For some reason each time i send the form I get this error message
I can't see the error!
But I tried this and it works ok.
import math
class MyForm(forms.Form):
title = forms.CharField(max_length=100, min_length=4)
story = forms.CharField(max_length=3000, min_length=4)
lat = forms.DecimalField(max_digits=25, decimal_places=20)
lng = forms.DecimalField(max_digits=25, decimal_places=20)
def clean_lat(self):
lat = self.cleaned_data['lat']
if math.ceil(float(lat)) <= 0:
raise forms.ValidationError("Enter your full story")
return self.cleaned_data['lat']
def clean_lng(self):
lng = self.cleaned_data['lng']
if math.ceil(float(lng)) <= 0:
raise forms.ValidationError("Enter your full story")
return self.cleaned_data['lng']

Categories

Resources