Change query parameter via select - django

I have a model Dish and a model Menu:
class MenuItem(models.Model):
dish_name=models.TextField(unique=False)
price=models.DecimalField(max_digits=5,decimal_places=2,blank=True)
main_ngredient=models.TextField(unique=False)
course=models.CharField(max_length=100)
menu=models.ForeignKey('Menu')
def __unicode__(self):
return name
class Menu(models.Model):
restaurant=models.TextField(unique=False)
year=models.IntegerField(unique=False)
location=models.TextField(unique=False)
status=models.CharField(unique=False,max_length=20)
NYPLid=models.IntegerField(unique=True)
def __unicode__(self):
return restaurant
def __period__(self):#adapted from http://stackoverflow.com/questions/2272149/round-to-5or-other-number-in-python
p=int(10*round(float(self.year)/10))
if p < self.year:
return "%s-%s"%(p,p+5)
else:
return "%s-%s"%(p-5,p)
period=property(__period__)
I have a view Search:
def search(request):
errors = []
form = SearchForm(request.GET)
if form.is_valid():
cd = form.cleaned_data
row = cd['row']
query = cd['query']
dish_row_dict = {"dish_name":'name__icontains=query',
"main_ingredient":"ingredient__icontains=query",
"course":"course__iexact=query"
}
menu_row_dict = {"year":'year__exact=query',
"period":'period__exact=query',
"location":'location__icontains=query',
"restaurant":"restaurant__icontains=query",
}
if row in dish_row_dict:
dishes = MenuItem.objects.filter(eval(dish_row_dict[row]))
elif row in menu_row_dict:
dishes = MenuItem.objects.filter(eval("menu__%s"%(menu_row_dict[row])))
return render_to_response("search_returns.html",{"dishes":dishes})
return render_to_response("search_page.html",{"form":form})
I have a form SearchForm:
class SearchForm(forms.Form):
row = forms.ChoiceField([("dish_name","Dish Name"),("year","Year"),
("period","Five-Year Period"),("location","Location"),
("main_ingredient","Main Ingredient"),("course","Course or Dish Type"),("restaurant","Restaurant"),])
query = forms.CharField())
I'd like users to be able to choose one of the select options, and then do a query based on their choice (e.g., search by dish name if they select "Dish Name".) They way I'm doing things now, with a dictionary+eval() isn't working (I know, I know, eval = bad, but I wasn't sure how else to do it!)
I feel like this isn't an exotic or uncommon thing, but I'll be damned if I can find anything about it online. I suspect the answer has something to do with MultiValueField or something like that, but I really don't understand how to make that work.
Any help would be greatly appreciated.

You can forget the =query and the eval() part and simply build a dict, and pass it as kwargs:
filter_keys = {
# menuitem queries
"dish_name":'name__icontains',
"main_ingredient": "ingredient__icontains",
"course": "course__iexact",
# menu queries
"year": 'menu__year__exact',
"period": 'menu__period__exact',
"location": 'menu__location__icontains',
"restaurant": "menu__restaurant__icontains",
}
...
if row in filter_keys:
dishes = MenuItem.objects.filter(**{filter_keys[row]: query})
The ** is a Python way of passing named parameters as a dict.

Related

Whats wrong with my search string? I am not getting proper data

I want to implement search on my django project. On my following queryset, with else condition its passing correct data. But with if condition whatever I search, it shows nothing.
def get_queryset(self):
category = self.request.GET['category']
query = self.request.GET['q']
if category == 'all':
products = Products.objects.filter(Q(name__icontains=query) | Q(category__name__icontains=query)).all()
else:
products = Products.objects.filter(Q(category__slug=category), Q(category__slug__icontains=self.request.GET['q']) | Q(name__icontains=self.request.GET['q']))

(Hidden field id) Select a valid choice. That choice is not one of the available choices. (Django)

I'm receiving this error when I try to submit two formsets. After I fill the form and click the save button, it gives the error:
(Hidden field id) Select a valid choice. That choice is not one of the available choices.
I'm trying to create dynamic form so that the user can add new sections and also new lectures inside the section when they click "Add" button. The adding new form function works well, I just have problem saving it to the database.
Views.py
def addMaterials(request, pk):
course = Course.objects.get(id=pk)
sections = CourseSection.objects.filter(course_id=pk)
materials = CourseMaterial.objects.filter(section__in=sections)
SectionFormSet = modelformset_factory(CourseSection, form=SectionForm, extra=0)
sectionformset = SectionFormSet(request.POST or None, queryset=sections)
MaterialFormSet = modelformset_factory(CourseMaterial, form=MaterialForm, extra=0)
materialformset = MaterialFormSet(request.POST or None, queryset=materials)
context = {
'course': course,
'sectionformset': sectionformset,
'materialformset': materialformset,
}
if request.method == "POST":
if all([sectionformset.is_valid() and materialformset.is_valid()]):
for sectionform in sectionformset:
section = sectionform.save(commit=False)
section.course_id = course.id
section.save()
for materialform in materialformset:
material = materialform.save(commit=False)
print(material)
material.section_id = section #section.id or section.pk also doesn't work
material.save()
return('success')
return render(request, 'courses/add_materials.html', context)
Forms.py
class SectionForm(forms.ModelForm):
class Meta:
model = CourseSection
fields = ['section_name', ]
exclude = ('course_id', )
class MaterialForm(forms.ModelForm):
class Meta:
model = CourseMaterial
fields = ['lecture_name', 'contents']
The second formset which is materialformset need the section id from the first formset hence why there is two loop in views.
Can someone help me to solve this. I'm not sure how to fix it.
This is the what I'm trying to do.
I'm new to django but I had to face with the same problem. My solution was to handle singularly each formset inside 'views.py'.
In the template.html, create a tag for each formset you have, than inside that tag put <input type="submit" name="form1">(Note that name is important and must be different with the respect of the form you are submitting).
Then in views.py, instead for writing if all([sectionformset.is_valid() and materialformset.is_valid()]), try like this:
if 'form1' in request.POST:
if sectionformset.is_valid():
sectionformset.save()
# other rows of your code
return('success')
if 'form2' in request.POST:
if materialformset.is_valid():
materialformset.save()
# etc. etc.

How to check which the fields of a form has been modified in Flask?

I am trying to figure out a way to check if there is any built-in Form method that would return true if the form has been modified in Flask/WTForms
I know that in Django Forms, we have that flexibility to use form.has_changed() method that would do exactly what i want.
I am trying to check if the form has been modified and if it is I would to do some database updates.
If anybody has any idea, please let me know about this or suggest the right approach to start with.
I haven't found any built-in Form methods, but here is my quirky approach how I check whether the form was changed, and which fields were changed.I'm just storing the whole original form in the additional StringField, and then comparing it with the new form. As I don't want this field to be displayed on the page, I'm changing it's style to style="visibility:hidden;display:none" in the html file
#app.route("/page", methods=["GET", "POST"])
def page(tag=None):
class Person(FlaskForm):
name = StringField("name")
age = StringField("name")
class Spouses(FlaskForm):
people = FieldList(FormField(JetsonForm), min_entries=0)
people_orig = StringField("people_orig")
submit = SubmitField("Submit")
data_from_database = get_data_from_database()
data = {'people': [], 'people_orig': json.dumps(data_from_database)}
for person_name in data_from_database:
data['people'].append(data_from_database[person_name])
form = Spouses(data=data)
people_orig_data = json.loads(form.people_orig.data)
people_new_data = dict()
for person in form.people:
people_new_data[person.name.data] = {
"name": persom.name.data,
"age": peron.age.data
}
if form.is_submitted():
for person_name in people_new_data:
for field in people_new_data[person_name]:
if people_new_data[person_name][field] != people_orig_data[cam][key]:
#update your database
return redirect(request.url)
return render_template("page.html",form=form)

Set default value for dynamic choice field

I have a form that asks the user to enter in their zip code. Once they do it sends them to another form where there is a field called 'pickup_date'. This gets the value of the zip from the previous field and gets all of the available pickup_dates that match that zip code into a ChoiceField. I set all of this within the init of the model form.
def __init__(self,*args,**kwargs):
super(ExternalDonateForm,self).__init__(*args,**kwargs)
if kwargs:
zip = kwargs['initial']['zip']
self.fields['pickup_date'] = forms.ChoiceField(choices = self.get_dates(zip))
elif self.errors:
zip = self.data['zip']
self.fields['pickup_date'] = forms.ChoiceField(choices = self.get_dates(zip))
The problem I have is when there are other errors on the form. I use the elif self.errors to regenerate the possible choices but it doesn't default to the original selected option. It goes back and defaults to the first choice. How can I make it so it's default option on form errors is what was originally posted?
Change self.fields['pickup_date'] to self.fields['pickup_date'].initial and see if that helps.
I got it to work after playing around for a while. Above, I was setting all the dynamic choices with a get_dates() function that returned a tuple. Instead of doing that I returned a field object like this using a customized ModelChoiceField instead of a regular ChoiceField....
class MyModelChoiceField(ModelChoiceField):
def label_from_instance(self, obj):
return obj.date.strftime('%a %b %d, %Y')
Dates function
def get_dates(self,zip):
routes = Route.objects.filter(zip=zip).values_list('route',flat=True)
pickups = self.MyModelChoiceField(queryset = PickupSchedule.objects.filter(
current_count__lt=F('specials'),
route__in=routes,
).order_by('date')
)
if not pickups:
pickups = (('----','No Pickups Available At This Time'),)
return pickups
in the init i set the value for self.fields['pickup_date'] like so..
self.fields['pickup_date'] = self.get_dates(zip)

How can I add exceptions for sorting Django models?

I've got a line in python:
streams = Stream.objects.filter(info__isnull = False)\
.order_by('-score', 'info__title')
I feed that to a RequestContext
c = RequestContext(request, {
'online_streams': streams.filter(online = True),
'offline_streams': streams.filter(online = False)
})
It sorts a table of ~50 rows by their score and title. This is good, but there is one row I would like to always be top. Simply fetching it beforehand and filtering it out of the line above, then give it to the RequestContext separately won't work, since I'd have to modify the template. Also I might change which row should be on top later, or perhaps make it multiple rows instead.
Is there a filter argument I can insert that says something in the lines of "Order all rows by score then title, except row ID 8, which should always be top"? Or could I perhaps change the order of the QuerySet manually after the filtering?
Thanks for any suggestions
You can override Model manager method:
class MyManager(models.Manager):
def filter(self, *args, **kwargs):
return super(MyManager, self).filter(*args, **kwargs).exclude(id=8).order_by(...)
then in model
class Stream(models.Model):
...
objects = MyManager()
EDIT
to make sure it is included you can query:
from django.db.models import Q
return super(MyManager, self).filter(Q(id=8) | Q(*args, **kwargs)).order_by(...)
I couldn't find a way to use query sets to do this, so I ended up converting the query set to a list and sorting it using the magic compare function. It ended up looking something like this:
The magic sorter, checking for names that should be stuck "on top":
# Comparison magic
def __cmp__(self, other):
# On top
if self.name in stream_ontop:
return 1
if other.name in stream_ontop:
return -1
# Score
if self.score > other.score:
return 1
if self.score < other.score:
return -1
# Title alphabetical
if self.info.title > other.info.title:
return -1
if self.info.title < other.info.title:
return 1
return 0
Then I fetched and sorted them like this from my model class
# Custom sorting
#staticmethod
def getAllSorted(online):
streams = list(Stream.objects.filter(online=online, info__isnull=False))
streams.sort(key=None, reverse=True)
return streams