Django: Initializing a FormSet of custom forms with instances - django

Given the following models:
class Graph(models.Model):
owner = models.ForeignKey(User)
def __unicode__(self):
return u'%d' % self.id
class Point(models.Model):
graph = models.ForeignKey(Graph)
date = models.DateField(primary_key = True)
abs = models.FloatField(null = True)
avg = models.FloatField(null = True)
def __unicode__(self):
return u'%s' % self.date
I am trying to create a form for editing lists of Points.
The HTML input tags require additional attributes to be set, so I am using the following custom form:
class PointForm(forms.ModelForm):
graph = forms.ModelChoiceField(queryset = Graph.objects.all(),
widget = forms.HiddenInput())
date = forms.DateField(widget = forms.HiddenInput(), label = 'date')
abs = forms.FloatField(widget = forms.TextInput(
attrs = {'class': 'abs-field'}),
required = False)
class Meta:
model = Point
fields = ('graph', 'date', 'abs') # Other fields are not edited.
def pretty_date(self):
return self.data.strftime('%B')
At this point I do not know how to pass instances of the Point class to a FormSet:
def edit(request):
PointFormSet = forms.formsets.formset_factory(PointForm, extra = 0)
if request.method == 'POST':
return
# Receive 3 points to edit from the database.
graph, res = Graph.objects.get_or_create(id = 1)
one_day = datetime.timedelta(days = 1)
today = datetime.date.today()
do_edit = []
for date in [today - (x * one_day) for x in range(3)]:
point, res = Point.objects.get_or_create(graph = graph, date = date)
do_edit.append(point)
formset = PointFormSet(????) # How is this initialized with the points?
I found a hack that somewhat works, but it leads to errors later on when trying to process the resulting POST data:
do_edit = []
for date in [today - (x * one_day) for x in range(3)]:
point, res = Point.objects.get_or_create(graph = graph, date = date)
data = point.__dict__.copy()
data['graph'] = graph
do_edit.append(data)
formset = PointFormSet(initial = do_edit)
How is this done correctly?
For the reference, my template looks like this:
<form action="" method="post">
{{ formset.management_form }}
<table>
<tbody>
{% for form in formset.forms %}
<tr>
<td>{{ form.graph }} {{ form.date }} {{ form.pretty_date }}:</td>
<td width="100%">{{ form.abs }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</form>

The trick is to use a "ModelFormset" instead of just a formset since they allow initialization with a queryset. The docs are here, what you do is provide a form=* when creating the model formset and queryset=* when your instantiating the formset. The form=* arguement is not well documented (had to dig around in the code a little to make sure it is actually there).
def edit(request):
PointFormSet = modelformset_factory(Point, form = PointForm)
qset = Point.objects.all() #or however your getting your Points to modify
formset = PointFormset(queryset = qset)
if request.method == 'POST':
#deal with posting the data
formset = PointFormset(request.POST)
if formset.is_valid():
#if it is not valid then the "errors" will fall through and be returned
formset.save()
return #to your redirect
context_dict = {'formset':formset,
#other context info
}
return render_to_response('your_template.html', context_dict)
So the code walks through easily. If the request is a GET then the instantiated form is returned to the user. If the request is a POST and the form is not .is_valid() then the errors "fall through" and are returned in the same template. If the request is a POST and the data is valid then the formset is saved.
Hope that helps.
-Will

If you only have one possible value which you want to set, or perhaps a closed of values, it is possible to set them after the user POSTS the data to your server using commit=False
Please consider the following code:
class UserReferralView(View):
ReferralFormSet = modelformset_factory(ReferralCode,
form=ReferralTokenForm, extra=1)
def get(self, request):
pass
def post(self, request):
referral_formset = UserUpdateView.ReferralFormSet(request.POST)
if referral_formset.is_valid():
instances = referral_formset.save(commit=False)
for instance in instances:
instance.user = request.user
instance.save()
return redirect(reverse('referrals.success_view'))
else:
return redirect(reverse('referrals.failure_view'))

Related

Passing default values into an unbound django form

I have two select classes that I am trying to create in an unbound form. The data selections are only relevant to the presentation that is created in the view, so are throwaways and do not need to be saved in a model.
The challenge I have is that I can pass in the field listings ok, but how do I set "default" checked / selected values so that the form becomes 'bound'?
views.py
def cards(request):
sort_name = []
sort_name.append("Alphabetic Order")
sort_name.append("Most Popular")
sort_name.append("Least Popular")
sort_name.append("Highest Win Rate")
sort_name.append("Lowest Win Rate")
sort_id = range(len(sort_name))
sort_list = list(zip(sort_id, sort_name))
<more code to make filt_list and zip it>
if request.method == 'POST':
form = cardStatsForm(request.POST, sortList=sort_list, filtList=filt_list)
if form.is_valid():
do something
else:
do something else
else:
form = cardStatsForm(filter_list, sort_list)
forms.py
class cardStatsForm(forms.Form):
def __init__(self, filterList, sortList, *args, **kwargs):
super(cardStatsForm, self).__init__(*args, **kwargs)
self.fields['filts'].choices = filterList
self.fields['filts'].label = "Select player rankings for inclusion in statistics:"
self.fields['sorts'].choices = sortList
self.fields['sorts'].label = "Choose a sort order:"
filts = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, choices=(), required=True)
sorts = forms.ChoiceField(choices=(), required=True)
The difficulty I am having is the the form fails the "is_valid" test since it is not bound, and I have the "required=true" setting (so that the user must select a checkbox / select a value), but I cannot enforce the logic since it seems the form is never 'bound'.
You can use django forms validation or pass defult value in your views.py. It will return unbound forms if value doesn't match with your default value.
let show you how to do it in your views.py:
error_message = None
default_value = "jhone"
if form.is_valid():
name = request.POST['name']
defult_name = jhone
if defult_name != name:
error_message = 'Name must be jhone'
if not error_message:
form.save() #it will only save forms if default value match
else:
do something else
context = {'error_message':error_message,
'default_value': default_value,
'form':form,
} #pass the context in your html template for showing default value and error message
in your .html
{{error_message}}
<input type=text name='name' {%if form.is_bound %} value="{{default_value}} {%endif%}">
I was able to correct my issue by adding "inital=0" and modifying my form call as outlined below:
forms.py
filts = forms.MultipleChoiceField(widget=forms.CheckboxSelectMultiple, choices=(), initial=0, required=True)
sorts = forms.ChoiceField(choices=(), initial=0, required=True)
views.py
if request.method == 'POST':
form = cardStatsForm(data=request.POST, sortList=sort_list, filterList=filter_list)
else:
form = cardStatsForm(filter_list, sort_list)

Django - This is field is required error

I am new to Django and trying to save some data from the form to the model. I want to insert into two models which have a foreign key constraint relationship (namely Idea and IdeaUpvotes) i.e. from a html template to a view.
My submit code is:
def submitNewIdea(request):
#get the context from the request
context = RequestContext(request)
print(context)
#A HTTP POST?
if request.method == 'POST':
form = submitNewIdeaForm(request.POST)
# Have we been provided with a valid form?
if form.is_valid():
# Save the new Idea to the Idea model
print(request.POST.get("IdeaCategory"))
print(request.POST.get("IdeaSubCategory"))
i = Idea( idea_heading = form["idea_heading"].value()
,idea_description = form["idea_description"].value()
,idea_created_by = form["idea_created_by"].value()
,idea_votes = form["idea_votes"].value()
,idea_category = request.POST.get("IdeaCategory") #value from dropdown
,idea_sub_category = request.POST.get("IdeaSubCategory") #value from dropdown
)
i.save()
# get the just saved id
print(Idea.objects.get(pk = i.id))
iu = IdeaUpvotes(idea_id = Idea.objects.get(pk = i.id)
,upvoted_by = form["upvoted_by"].value()
,upvoted_date = timezone.now() )
iu.save()
form.save(commit = True)
# Now call the index() view.
# The user will be shown the homepage.
return index(request)
else:
# The supplied form contained errors - just print them to the terminal.
print (form.errors)
else:
# If the request was not a POST, display the form to enter details.
form = submitNewIdeaForm()
# Bad form (or form details), no form supplied...
# Render the form with error messages (if any).
return render(request,'Ideas/Index.html',{'form' :form})
form.py --->
class submitNewIdeaForm(forms.ModelForm):
idea_heading = forms.CharField(label = "idea_heading",max_length =1000,help_text= "Please enter the idea heading.")
idea_description= forms.CharField(label = "idea_description",max_length =1000,help_text= "Please enter the idea description.",widget=forms.Textarea)
idea_created_by=forms.CharField(max_length =200, widget = forms.HiddenInput(), initial='wattamw')
idea_votes = forms.IntegerField(widget=forms.HiddenInput(), initial=1)
upvoted_by=forms.CharField(max_length =200, widget = forms.HiddenInput(), initial='abcde')
"""
#commented code
#idea_category_name = forms.CharField(label = "idea_category_name",max_length =250,help_text= "Please select an Idea Category.")
#idea_sub_category = forms.CharField(label = "idea_sub_category",max_length =250,help_text= "Please select an Idea Sub Category.")
idea_category_name = forms.ModelChoiceField(
queryset = IdeaCategory.objects.all(),
widget=autocomplete.ModelSelect2(url='category-autocomplete'))
idea_sub_category = forms.ModelChoiceField(
queryset = IdeaSubCategory.objects.all(),
widget = autocomplete.ModelSelect2(
url='subcategory-autocomplete',
forward = (forward.Field('idea_category_name','id'),)))
"""
class Meta:
model = Idea
fields = ('idea_heading','idea_description','idea_created_by','idea_votes','idea_category_name','idea_sub_category')
class Meta:
model = IdeaUpvotes
fields = ('upvoted_by',)
def __init__(self,*args,**kwargs):
super(submitNewIdeaForm,self).__init__(*args,**kwargs)
self.fields['idea_category_name'] = forms.ModelChoiceField(
queryset = IdeaCategory.objects.all(),
widget=autocomplete.ModelSelect2(url='category-autocomplete'))
self.fields['idea_sub_category'] = forms.ModelChoiceField(
queryset = IdeaSubCategory.objects.all(),
widget = autocomplete.ModelSelect2(
url='subcategory-autocomplete',
forward = (forward.Field('idea_category_name','id'),)))
I am able to print the values and see that they are passed,but I still get the following error :
Error Description
I have removed any foreign key references to the table, the fields are simple character fields.
Please help me out.
Thanks.
In the first place, your form validation is failing. It seems to me that your form template may be wrong.
The second thing is that you don't use Django forms properly. All you need to do to achieve the functionality you are looking for is to use ModelForm and let the form's save method to create the object for you. All you need to do is:
Associate your SubmitNewIdeaForm with the Idea model:
# forms.py
class SubmitNewIdeaForm(ModelForm):
class Meta:
model = Idea
fields = (
'idea_heading',
'idea_description',
'idea_created_by',
'idea_votes',
'idea_category',
'idea_sub_category'
)
Render the form
#form_template.html
<form action="{% url 'your_url' %}" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>
Finally jsut check if the form is valid and call form.save() like so:
def submitNewIdea(request):
if form.is_valid():
form.save()
That's it! I hope that I helped you.
Cheers!
Finished = models.IntegerField('Finished percentage', error_messages={'required':''})
Worked for me.

How to retain select option value after submit in django?

I have created select tag with four option values(1,2,3,4).When I select 4 and press submit it changes back to 1.Select option contains quantity of the product needed for the user.So how to retain option value after pressing submit button.I tried like this,after I press submit button value changes back to 1.Is there any way to overcome this problem?
My template file,
<label for="quantity">Quantity</label>
<select id="quantity" name="quantity">
<option value="1" {% if quantity == '1' %}selected{% endif %}>1</option>
<option value="2" {% if quantity == '2' %}selected{% endif %}>2</option>
<option value="3" {% if quantity == '3' %}selected{% endif %}>3</option>
<option value="4" {% if quantity == '4' %}selected{% endif %}>4</option>
</select>
<input type="submit" value="Buy"/>
UPDATE:
forms.py,
class SortForm(forms.Form):
RELEVANCE_CHOICES = (
(1,'1'),(2, '2'),(3,'3'), (4,'4'),(5,'5'),
)
sort = forms.ChoiceField(choices = RELEVANCE_CHOICES,label='Quantity')
views.py,
from .forms import SortForm
#csrf_protect
def buy_book(request,pk):
form = SortForm(request.POST or None)
my_products = Add_prod.objects.filter(pk=pk)
#Add_prod is the model class name
context = {"products":my_products}
if request.POST.get('quantity'):
for i in my_products:
rate= i.price
#price is the column name in the model class
u_quantity = request.POST.get('quantity')
Quantity=int(u_quantity)
total = rate*Quantity
context = {
"products":my_products,
"Total":total,
"form": form
}
return render(request,"buy_book.html",context)
In the template file I added this line,
{{form.as_p}}
Now I am getting blank output.I think the form is not recognising in the template.
The problem here that your template is just showing data, it doesn't know anything about state. So if you want to achieve this kind of behavior you need to supply all required data from backend. Also as #solarissmoke mentioned you should use django forms.
for exmaple(Pseudo code below)
def my_view(request):
if request.method == 'POST':
form = MyForm(request.data)
if form.is_valid():
form.save()
redirect(reverse('myview'))
else:
form = MyForm(instance) # <- instance is object with previously saved data
return render(request, 'my_template.html' , {'form': form})
Second Part
def buy_book(request,pk):
form = SortForm(request.POST or None)
my_products = Add_prod.objects.filter(pk=pk)
#Add_prod is the model class name
context = {"products":my_products}
if request.POST.get('quantity'):
for i in my_products:
rate= i.price
#price is the column name in the model class
u_quantity = request.POST.get('quantity')
Quantity=int(u_quantity)
total = rate*Quantity
context = {
"products":my_products,
"Total":total,
"form": form # <- here is problem
}
return render(request,"buy_book.html",context)
You are adding form to context inside if request.method == 'POST'. It should like this
def buy_book(request,pk):
form = SortForm(request.POST or None)
my_products = Add_prod.objects.filter(pk=pk)
#Add_prod is the model class name
context = {"products":my_products, 'form': form} # <- here
if request.POST.get('quantity'):
for i in my_products:
rate= i.price
#price is the column name in the model class
u_quantity = request.POST.get('quantity')
Quantity=int(u_quantity)
total = rate*Quantity
context = {
"products":my_products,
"Total":total,
}
return render(request,"buy_book.html",context)
In your view, you only ever add the form to the context data if there is a quantity in the post data, you should add this to the context regardless since it is required for your view.
You should also actually use the form, so instead of checking the post data, check the form for validity and then use its cleaned data.
def buy_book(request,pk):
form = SortForm(request.POST or None)
my_products = Add_prod.objects.filter(pk=pk)
#Add_prod is the model class name
context = {"products":my_products,
'form': form}
if form.is_valid():
for i in my_products:
rate= i.price
#price is the column name in the model class
u_quantity = form.cleaned_data.get('sort', 0)
Quantity=int(u_quantity)
total = rate*Quantity
context['total'] = total
return render(request,"buy_book.html",context)
'int' object is not iterable
Probably because your sort field isn't a list of tuples
choices = [(i, i) for i in range(1,6)]

Django ListView - Form to filter and sort

My Goal
A site that list all my Updates (model) in a table
Dont display all models at once (pagination - maybe 10 per page)
Filter and sort the list
My thoughts
I can use ListView to get a set of all my Updates
Use paginate_by = 10
Use a form to set order_by or filter in my QuerySet
My Problem
I am not sure how to add an form to modify my QuerySet with filter and sortings. My Idea was to modify the Query in get_queryset with additional filter and order_by.
My View
class MyView(ListView):
model = Update
template_name = "updates/update.html"
paginate_by = 10
def get_queryset(self):
return Update.objects.filter(
~Q(state=Update.STATE_REJECTED),
~Q(state=Update.STATE_CANCELED),
~Q(state=Update.STATE_FINISHED),
).order_by(
'planned_release_date'
)
My Idea
Something like this. I know it's not working like this ... just to illustrate
class MyView(ListView):
model = Update
template_name = "updates/update.html"
paginate_by = 10
def post(self, request, *args, **kwargs):
new_context = Update.objects.filter(
request.POST.get("filter"),
).order_by(
request.POST.get("sorting"),
)
def get_queryset(self):
return Update.objects.filter(
~Q(state=Update.STATE_REJECTED),
~Q(state=Update.STATE_CANCELED),
~Q(state=Update.STATE_FINISHED),
).order_by(
'planned_release_date'
)
You don't need post. Pass the filter value and order_by in the url for example:
.../update/list/?filter=filter-val&orderby=order-val
and get the filter and orderby in the get_queryset like:
class MyView(ListView):
model = Update
template_name = "updates/update.html"
paginate_by = 10
def get_queryset(self):
filter_val = self.request.GET.get('filter', 'give-default-value')
order = self.request.GET.get('orderby', 'give-default-value')
new_context = Update.objects.filter(
state=filter_val,
).order_by(order)
return new_context
def get_context_data(self, **kwargs):
context = super(MyView, self).get_context_data(**kwargs)
context['filter'] = self.request.GET.get('filter', 'give-default-value')
context['orderby'] = self.request.GET.get('orderby', 'give-default-value')
return context
Make sure you give proper default value to filter and orderby
Example form (you can modify this to your need):
<form method="get" action="{% url 'update-list' %}">
<p>Filter: <input type="text" value={{filter}} name="filter"/></p>
<p>order_by: <input type="text" value={{orderby}} name="orderby"/></p>
<p><input type="submit" name="submit" value="submit"/></p>
</form>
I am wondering why nobody mentioned here this cool library: django-filter https://github.com/carltongibson/django-filter
you can define your logic for filtering very clean and get fast working forms etc.
demo here: https://stackoverflow.com/a/46492378/953553
I posted this elsewhere but I think this adds to the selected answer.
I think you would be better off doing this via get_context_data. Manually create your HTML form and use GET to retrieve this data. An example from something I wrote is below. When you submit the form, you can use the get data to pass back via the context data. This example isn't tailored to your request, but it should help other users.
def get_context_data(self, **kwargs):
context = super(Search, self).get_context_data(**kwargs)
filter_set = Gauges.objects.all()
if self.request.GET.get('gauge_id'):
gauge_id = self.request.GET.get('gauge_id')
filter_set = filter_set.filter(gauge_id=gauge_id)
if self.request.GET.get('type'):
type = self.request.GET.get('type')
filter_set = filter_set.filter(type=type)
if self.request.GET.get('location'):
location = self.request.GET.get('location')
filter_set = filter_set.filter(location=location)
if self.request.GET.get('calibrator'):
calibrator = self.request.GET.get('calibrator')
filter_set = filter_set.filter(calibrator=calibrator)
if self.request.GET.get('next_cal_date'):
next_cal_date = self.request.GET.get('next_cal_date')
filter_set = filter_set.filter(next_cal_date__lte=next_cal_date)
context['gauges'] = filter_set
context['title'] = "Gauges "
context['types'] = Gauge_Types.objects.all()
context['locations'] = Locations.objects.all()
context['calibrators'] = Calibrator.objects.all()
# And so on for more models
return context
This is how we do it, that way you get validation/type conversion as well:
class UnitList(PermissionRequiredMixin, ListView):
""" Class based view to show a list of all buildings for a specific user """
model = Unit
ordering = ['building', 'unit']
paginate_by = 100
# Access
permission_required = ['core.manager_perm']
raise_exception = True # If true, give access denied message rather than redirecting to login
def get_queryset(self):
try:
units = self.model.objects.filter(building__company=self.request.user.profile.company)
except Profile.DoesNotExist:
units = self.model.objects.none()
form = UnitSearchForm(self.request.GET)
if form.is_valid():
filters = {}
address = form.cleaned_data['address']
neighborhood = form.cleaned_data['neighborhood']
beds = form.cleaned_data['beds']
amenity = form.cleaned_data['amenity']
if address:
filters['building__street_index__istartswith'] = compute_street_address_index(address)
if neighborhood:
filters['building__neighborhood__icontains'] = neighborhood
if beds:
filters['beds'] = beds
if amenity:
filters['unit_amenities__name__iexact'] = amenity
units = units.filter(**filters)
return units.select_related('building').order_by(*self.ordering)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['form'] = UnitSearchForm(self.request.GET)
return context

Django: Search form in Class Based ListView

I am trying to realize a Class Based ListView which displays a selection of a table set. If the site is requested the first time, the dataset should be displayed. I would prefer a POST submission, but GET is also fine.
That is a problem, which was easy to handle with function based views, however with class based views I have a hard time to get my head around.
My problem is that I get a various number of error, which are caused by my limited understanding of the classed based views. I have read various documentations and I understand views for direct query requests, but as soon as I would like to add a form to the query statement, I run into different error. For the code below, I receive an ValueError: Cannot use None as a query value.
What would be the best practise work flow for a class based ListView depending on form entries (otherwise selecting the whole database)?
This is my sample code:
models.py
class Profile(models.Model):
name = models.CharField(_('Name'), max_length=255)
def __unicode__(self):
return '%name' % {'name': self.name}
#staticmethod
def get_queryset(params):
date_created = params.get('date_created')
keyword = params.get('keyword')
qset = Q(pk__gt = 0)
if keyword:
qset &= Q(title__icontains = keyword)
if date_created:
qset &= Q(date_created__gte = date_created)
return qset
forms.py
class ProfileSearchForm(forms.Form):
name = forms.CharField(required=False)
views.py
class ProfileList(ListView):
model = Profile
form_class = ProfileSearchForm
context_object_name = 'profiles'
template_name = 'pages/profile/list_profiles.html'
profiles = []
def post(self, request, *args, **kwargs):
self.show_results = False
self.object_list = self.get_queryset()
form = form_class(self.request.POST or None)
if form.is_valid():
self.show_results = True
self.profiles = Profile.objects.filter(name__icontains=form.cleaned_data['name'])
else:
self.profiles = Profile.objects.all()
return self.render_to_response(self.get_context_data(object_list=self.object_list, form=form))
def get_context_data(self, **kwargs):
context = super(ProfileList, self).get_context_data(**kwargs)
if not self.profiles:
self.profiles = Profile.objects.all()
context.update({
'profiles': self.profiles
})
return context
Below I added the FBV which does the job. How can I translate this functionality into a CBV?
It seems to be so simple in function based views, but not in class based views.
def list_profiles(request):
form_class = ProfileSearchForm
model = Profile
template_name = 'pages/profile/list_profiles.html'
paginate_by = 10
form = form_class(request.POST or None)
if form.is_valid():
profile_list = model.objects.filter(name__icontains=form.cleaned_data['name'])
else:
profile_list = model.objects.all()
paginator = Paginator(profile_list, 10) # Show 10 contacts per page
page = request.GET.get('page')
try:
profiles = paginator.page(page)
except PageNotAnInteger:
profiles = paginator.page(1)
except EmptyPage:
profiles = paginator.page(paginator.num_pages)
return render_to_response(template_name,
{'form': form, 'profiles': suppliers,},
context_instance=RequestContext(request))
I think your goal is trying to filter queryset based on form submission, if so, by using GET :
class ProfileSearchView(ListView)
template_name = '/your/template.html'
model = Person
def get_queryset(self):
name = self.kwargs.get('name', '')
object_list = self.model.objects.all()
if name:
object_list = object_list.filter(name__icontains=name)
return object_list
Then all you need to do is write a get method to render template and context.
Maybe not the best approach. By using the code above, you no need define a Django form.
Here's how it works : Class based views separates its way to render template, to process form and so on. Like, get handles GET response, post handles POST response, get_queryset and get_object is self explanatory, and so on. The easy way to know what's method available, fire up a shell and type :
from django.views.generic import ListView if you want to know about ListView
and then type dir(ListView). There you can see all the method defined and go visit the source code to understand it. The get_queryset method used to get a queryset. Why not just define it like this, it works too :
class FooView(ListView):
template_name = 'foo.html'
queryset = Photo.objects.all() # or anything
We can do it like above, but we can't do dynamic filtering by using that approach. By using get_queryset we can do dynamic filtering, using any data/value/information we have, it means we also can use name parameter that is sent by GET, and it's available on kwargs, or in this case, on self.kwargs["some_key"] where some_key is any parameter you specified
Well, I think that leaving validation to form is nice idea. Maybe not worth it in this particular case, because it is very simple form - but for sure with more complicated one (and maybe yours will grow also), so I would do something like:
class ProfileList(ListView):
model = Profile
form_class = ProfileSearchForm
context_object_name = 'profiles'
template_name = 'pages/profile/list_profiles.html'
profiles = []
def get_queryset(self):
form = self.form_class(self.request.GET)
if form.is_valid():
return Profile.objects.filter(name__icontains=form.cleaned_data['name'])
return Profile.objects.all()
This is similar to #jasisz 's approach, but simpler.
class ProfileList(ListView):
template_name = 'your_template.html'
model = Profile
def get_queryset(self):
query = self.request.GET.get('q')
if query:
object_list = self.model.objects.filter(name__icontains=query)
else:
object_list = self.model.objects.none()
return object_list
Then all you have to do on the html template is:
<form method='GET'>
<input type='text' name='q' value='{{ request.GET.q }}'>
<input class="button" type='submit' value="Search Profile">
</form>
This has been explained nicely on the generic views topic here Dynamic filtering.
You can do filtering through GET, I don't think you can use POST method for this as ListView is not inherited from edit mixings.
What you can do is:
urls.py
urlpatterns = patterns('',
(r'^search/(\w+)/$', ProfileSearchListView.as_view()),
)
views.py
class ProfileSearchListView(ListView):
model = Profile
context_object_name = 'profiles'
template_name = 'pages/profile/list_profiles.html'
profiles = []
def get_queryset(self):
if len(self.args) > 0:
return Profile.objects.filter(name__icontains=self.args[0])
else:
return Profile.objects.filter()
I think that the error you are getting is because your form doesn't require the name field. So, although the form is valid, the cleaned_data for your name field is empty.
These could be the problematic lines:
if form.is_valid():
self.show_results = True
self.profiles = Profile.objects.filter(name__icontains=form.cleaned_data['name'])
If I were you, I would try changing the line:
self.profiles = Profile.objects.filter(name__icontains=form.cleaned_data['name'])
to this:
self.profiles = Profile.objects.none()
If you stop receiving errors (and your template receives an empty object_list), the problem you have is what I said before: name field not required.
Let us know if this doesn't work!
Search on all fields in model
class SearchListView(ItemsListView):
# Display a Model List page filtered by the search query.
def get_queryset(self):
fields = [m.name for m in super(SearchListView, self).model._meta.fields]
result = super(SearchListView, self).get_queryset()
query = self.request.GET.get('q')
if query:
result = result.filter(
reduce(lambda x, y: x | Q(**{"{}__icontains".format(y): query}), fields, Q())
)
return result
def get_queryset(self):
query_name = self.request.GET.get('query', '')
object_list = Product.objects.filter(
Q(title__icontains=query_name)
)
return object_list
<form action="" method="GET">
{% csrf_token %}
<input type="text" name="query" placeholder="Search keyword">
<i class="ti-search"></i>
</form>