Flexible Django Filter (django-filter package) - django

I'm currently implementing some filters and I'm facing multiple problems. First of all my checkbox works when clicking on the brand title, not the actual:
this is my .html code:
<form method="get">
<div class="toggle-list product-categories">
<h6 class="title">Sorteer</h6>
<div class="shop-submenu">
<ul>
{{ filter.form.order_by }}
</ul>
</div>
</div>
<div class="toggle-list product-categories">
<h6 class="title">Merk(en)</h6>
<div class="shop-submenu">
<ul>
{% for f in filter.form.brand %}
<li>
<input type="checkbox" name="brand" id="brand">
<label for="brand">{{ f }}</label>
</li>
{% endfor %}
</ul>
</div>
</div>
<input type="submit" value="Filter">
</form>
my filter in filters.py:
class SortFilter(django_filters.FilterSet):
ORDER_BY_CHOICES = (
('-discount_sort', 'Hoogste korting'),
('-new_price', 'Hoogste prijs'),
('new_price', 'Laagste prijs'),
)
order_by = django_filters.ChoiceFilter(label='Sorteer op', choices=ORDER_BY_CHOICES, method='filter_by_order',
empty_label=None)
brand = django_filters.ModelMultipleChoiceFilter(queryset=Product.objects
.order_by('brand')
.filter(categorie='eiwitten')
.values_list('brand', flat=True).distinct()
, widget=forms.CheckboxSelectMultiple, required=False)
class Meta:
model = Product
fields = ['brand']
def filter_by_order(self, queryset, name, value):
return queryset.order_by(value)
view function for this certain page:
def eiwit(request):
# filter alleen eiwitproducten
eiwit_list = ['eiwitten']
eiwit_filter = Q()
for item in eiwit_list:
eiwit_filter = eiwit_filter | Q(categorie=item)
products = models.Product.objects.filter(eiwit_filter)
product_amount = len(products)
# sorteer filter
filtered = SortFilter(
request.GET,
queryset=products
)
# paginator
paginator = Paginator(filtered.qs, 12)
page = request.GET.get('page')
try:
response = paginator.page(page)
except PageNotAnInteger:
response = paginator.page(1)
except EmptyPage:
response = paginator.page(paginator.num_pages)
product_front_end = {
'final_products': response,
'filter': filtered,
'count': product_amount,
}
return render(request, 'producten/eiwit.html', product_front_end)
Second of all, I would like the filter to be more generic. As in all categories have different amount of brands. As you can see I currently create options by selecting all the 'brand' values within the 'eiwitten' categorie (in filters.py). Is there a way to call the filter in views.py with a variable (like 'eiwittien'). Which then will be used to adjust the filter in filter.py according to the page it is on? This way I have a dynamic filter for all categories.

This might help with some of your issues. Your input tag is missing information on the name/id fields. You should be able to populate those by adding template tags to fill the values.
<input type="checkbox" id="BSN" name="BSN"> <label for="BSN">BSN</label>

Related

Values from the second form - depending on the selected value of the first?

I have a Django form. In which there are two select fields from several values. The data from them is contained in the Django model database.
The first field of the form contains car brands.
In the second field of the form, I would like to display car models - depending on the car brand selected above.
How can I make the data appear in the second field of the form depending on the selected value of the first field?
class Model_for_form(models.Model):
name_1 = models.CharField(max_length=150, verbose_name="Name_1")
name_2 = models.CharField(max_length=150, verbose_name="Name_2")
def __str__(self):
return self.name_1, self.name_2
class Form_1(forms.ModelForm):
class Meta:
model = Model_for_form
fields = "__all__"
def form_1(request):
context = {}
form = Model_for_form(request.POST or None)
if form.is_valid():
form.save()
return redirect("form_0")
context['form_1'] = form
return render(request, "form_1.html", context)
{% extends "base.html" %}
{% block content %}
<hr/>
<div class="row">
<div class="col-6">
<form method="POST" class="post-form">
{% csrf_token %} {{form_1.as_p}}
<button type="submit" class="save btn btn-light">button</button>
</form>
</div>
</div>
<br/>

Django - In a ModelForm, display data coming from a model linked with M2M relationship

I would like to add information in a form, coming from the model linked with a M2M relationship to the model I'm updating.The form works properly, but I'm not able to add any information.
Here is what I get:
Here is the expected result:
My solution: finally, I updated __str__() mthod in UserGroup model to display what I expect (but, at this stage, I lost the dynamic part and my view does not work anymore :-/)
The main model is Event and it's linked to Groups thanks to this relationship; in the form, all groups are listed and displayed with checkboxes, but I'm only able to display the groups' name, no other information.
It looks like I miss some data / information: the group name is displayed only because I use {{grp}}} (see below) but it has no attribute / filed available, even if it is initialized with a query from the group model.
I envisaged a workaround (see below) because my first tries made me consider this kind of solution, but I'm not able to reproduce what I did :-/
Any idea of what I did wrong, or any advice to manage this? Thanks in advance.
Here are related code parts.
Models:
class UserGroup(models.Model):
company = models.ForeignKey(
Company, on_delete=models.CASCADE, verbose_name="société"
)
users = models.ManyToManyField(UserComp, verbose_name="utilisateurs", blank=True)
group_name = models.CharField("nom", max_length=100)
weight = models.IntegerField("poids", default=0)
hidden = models.BooleanField(default=False)
def __str__(self):
return self.group_name
class Event(models.Model):
company = models.ForeignKey(
Company, on_delete=models.CASCADE, verbose_name="société"
)
groups = models.ManyToManyField(UserGroup, verbose_name="groupes", blank=True)
rules = [("MAJ", "Majorité"), ("PROP", "Proportionnelle")]
event_name = models.CharField("nom", max_length=200)
event_date = models.DateField("date de l'événement")
slug = models.SlugField()
current = models.BooleanField("en cours", default=False)
quorum = models.IntegerField(default=33)
rule = models.CharField(
"mode de scrutin", max_length=5, choices=rules, default="MAJ"
)
class Meta:
verbose_name = "Evénement"
constraints = [
models.UniqueConstraint(fields=["company_id", "slug"], name="unique_event_slug")
]
def __str__(self):
return self.event_name
Form:
class EventDetail(forms.ModelForm):
groups = forms.ModelMultipleChoiceField(
label = "Liste des groupes",
queryset = None,
widget = forms.CheckboxSelectMultiple,
required = False
)
class Meta:
model = Event
fields = ['event_name', 'event_date', 'quorum', 'rule', 'groups']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
instance = kwargs.get('instance', None)
self.fields['groups'].queryset= UserGroup.objects.\
filter(company=instance.company).\
order_by('group_name')
View:
#user_passes_test(lambda u: u.is_superuser or (u.id is not None and u.usercomp.is_admin))
def adm_event_detail(request, comp_slug, evt_id=0):
'''
Manage events creation and options
'''
company = Company.get_company(request.session['comp_slug'])
# all_groups = list(UserGroup.objects.filter(company=company, hidden=False).order_by('group_name').values())
if evt_id > 0:
current_event = Event.objects.get(id=evt_id)
event_form = EventDetail(request.POST or None, instance=current_event)
else:
event_form = EventDetail(request.POST or None)
event_form.fields['groups'].queryset = UserGroup.objects.\
filter(company=company, hidden=False).\
order_by('group_name')
if request.method == 'POST':
if event_form.is_valid():
if evt_id == 0:
# Create new event
event_data = {
"company": company,
"groups": event_form.cleaned_data["groups"],
"event_name": event_form.cleaned_data["event_name"],
"event_date": event_form.cleaned_data["event_date"],
"quorum": event_form.cleaned_data["quorum"],
"rule":event_form.cleaned_data["rule"]
}
new_event = Event.create_event(event_data)
else:
new_event = event_form.save()
else:
print("****** FORMULAIRE NON VALIDE *******")
print(event_form.errors)
return render(request, "polls/adm_event_detail.html", locals())
HTML (I did not put each parts of the 'accordion' widget, I do not think they have anything to do with the problem):
{% if evt_id %}
<form action="{% url 'polls:adm_event_detail' company.comp_slug evt_id %}" method="post">
{% else %}
<form action="{% url 'polls:adm_create_event' company.comp_slug %}" method="post">
{% endif %}
{% csrf_token %}
<!-- Hidden field where the referer is identified to go back to the related page after validation -->
<input type="hidden" name="url_dest" value="{{ url_dest }}" />
<br>
<!-- Accordion -->
<div id="eventDetails" class="accordion shadow">
<div class="card">
<div class="card-header bg-white shadow-sm border-0">
<h6 class="mb-0 font-weight-bold">
Evénement
</h6>
</div>
<div class="card-body p-5">
<p>Nom : {{event_form.event_name}} </p>
<p>Date : {{event_form.event_date}} </p>
</div>
</div>
<!-- Accordion item 2 - Event's groups -->
<div class="card">
<div id="headingTwo" class="card-header bg-white shadow-sm border-0">
<h6 class="mb-0 font-weight-bold">
<a href="#" data-toggle="collapse" data-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo" class="d-block position-relative collapsed text-dark collapsible-link py-2">
Groupes d'utilisateurs
</a>
</h6>
</div>
<div id="collapseTwo" aria-labelledby="headingTwo" data-parent="#eventDetails" class="collapse show">
<div class="card-body p-5">
<p>Sélectionnez le(s) groupe(s) d'utilisateurs participants à l'événement :</p>
<ul>
{% for grp in event_form.groups %}
<li>{{ grp }}
{{ grp.weight }}
{{ grp.hidden }}
{{ grp.nb_users }}
</li>
{% endfor %}
</ul>
<p></p>
</div>
</div>
</div>
</div> <!-- Accordion end -->
<button class="btn btn-success mt-5" type="submit">{% if evt_id %}Mettre à jour{% else %}Créer{% endif %}</button>
&nbsp &nbsp &nbsp
<a class="btn btn-secondary back_btn mt-5" href="*">Annuler</a>
<div class="row">
<div hidden>
<!-- List of groups in event -->
{{ event_form.group_list }}
</div>
</div>
</form>
Workaround
If it's not possible to achieve this directly, I thought to a workaround that would be implemented in several parts:
Create a list almost like the queryset: group_list = UserGroup.objects.filter(company=instance.company).order_by('group_name').values()
I already know I can display each group with its details and a checkbox
on client side (javascript), I manage an hidden list that would be part of the form, with the ID of each selected group. That means that the list will be dynamically updated when a box is checked on unchecked
on the POST request, read the list to update the group attribute of updated event.
I would have prefered the users actions having effect directly to the form, but I know this could work
You're accessing the form's groups field, not the model instance. The form field doesn't have any relationship to other models, it's just a field. You can access the underlying instance of the model form using form.instance.
Also note that you get a relationship manager object when querying related models. Hence, use .all to query all groups.
Try
<ul>
{% for grp in event_form.instance.groups.all %}
<li>{{ grp }}
{{ grp.weight }}
{{ grp.hidden }}
{{ grp.nb_users }}
</li>
{% endfor %}
</ul>

Django Choice Form add selected options

Hi all its my code if i choice in select list django work but i want after submit choices not be change.
After submit first choices again seeing.
My Form
class PostSorguForm(forms.Form):
HIKAYE_CHOICES=(('1','En Son Çıkanlar'),('2','En Çok Okunanlar'))
sorgu_form = forms.ChoiceField(choices=HIKAYE_CHOICES,required=False)
My view
class ArticleListView(FormMixin,ListView):
context_object_name = 'articles'
template_name = 'includes/article/article-list.html'
paginate_by = 15
form_class= PostSorguForm
def get_queryset(self):
queryset = Article.objects.all()
if self.request.GET.get("sorgu_form"):
selection = self.request.GET.get("sorgu_form")
if selection == "2":
queryset = Article.objects.all().order_by('-hit_count_generic__hits')
else:
queryset=Article.objects.filter(published=True).order_by('created_date').reverse()
return queryset
my template
<form method="GET" action="">
<div class="form-group">
<select class="form-control" name="sorgu_form" id="id_sorgu_form" onchange="this.form.submit()">
{% for x,y in form.fields.sorgu_form.choices %}
<option value="{{x}}">{{y}}</option>
{% endfor %}
</select>
</div>
</form>
i want after query option selected
i search a hours and i found now it's work
if you have another idea please write
<option value="{{x}}" {% if '?sorgu_form=2' in request.get_full_path %}selected{% endif %}>{{y}}</option>

Django-Filter | Looping over form fields

I am using django-filter which works as intended. I am however trying to loop over the form fields but the standard django attributes aren't working. The only one that does is {{ form.id_for_label }}. Am I missing something?
My template code:
<form action="" method="get">
{% for form in forms.form.product_colours %}
<div class="form-group">
<input type="checkbox" name="product_colours" id="{{ form.id_for_label }}" value="{{ form.value }}">
<label for="{{ form.id_for_label }}">{{ form.label }}</label>
</div>
{% endfor %}
<input type="submit" />
</form>
The reason I don't want to simply use {{ form }} is because it loads the <label> before the <input> whereas I'd need it to work viceversa to fit into my styling. I would prefer not to have to change the styling.
In case they are needed, here are my (simplified) models, FilterSet and my view:
Model:
class ProductColour(models.Model):
name = models.CharField(max_length=255)
colour_hex = models.CharField(max_length=16, default="#FFF")
def __str__(self):
return self.name
class Product(Page):
# My product fields
product_colours = ParentalManyToManyField('products.ProductColour', blank=True)
FilterSet:
class ProductFilter(django_filters.FilterSet):
product_colours = django_filters.ModelMultipleChoiceFilter(name="product_colours",
label="Product Colour",
widget=forms.CheckboxSelectMultiple,
queryset=ProductColour.objects.all(),
conjoined=False)
class Meta:
model = Product
fields = ['product_colours']
View:
def get_context(self, request):
context = super(Page, self).get_context(request)
all_products = ProductFilter(request.GET, queryset=Product.objects.live()).qs
forms = ProductFilter(request.GET, queryset=Product.objects.live())
paginator = Paginator(all_products, 9) # Show 9 resources per page
page = request.GET.get('page')
try:
products = paginator.page(page)
except PageNotAnInteger:
products = paginator.page(1)
except EmptyPage:
products = paginator.page(paginator.num_pages)
context['forms'] = forms
context['products'] = products
return context
In case you are wondering what the ParentalManyToManyField is, it's a Wagtail CMS class.
OK so I have found the solution - as user #Vicmathur suggested in the comments above I tried adding {% for check in form %}{% endfor %} and it threw up a 'BoundWidget' object is not iterable error.
So I googled BoundWidget and found the related Django documentation.
Instead of trying to access the data through, for example, {{ form.value }}, I need to specify{{ form.data.value }}

Django dropbox dynamic error

I have a page that display all the objects acorrding to the catergory the students pick.
I implemented a pagination on the site to split the objects at different pages.
The problem occurs when the students pick a catergory from the dropbox and tries to flick through the pagination for new and old entries.
The reason this happens because everytime the user picks a catergory from the dropbox , the dropbox get reset once it retrieve the objects. So when users try to flick through objects using the pagination . The pagination doesn't know what data to retrieve because the dropbox catergory get reset and redirec the users to a blank page.
A solution to this is to program the dropbox to remain static for the choices the users make so when the users flicks through the data split by the pagination , the pagination know can retrieve objects according to the dropbox.
I can't figure out how to make this dropbox remain static for the choices the users make.
my views.py
def BoardFinder(request):
form = BoardFinderForm(request.POST)
fo = BoardFinderForm()
if form.is_valid():
Category = form.cleaned_data['Category']
posts = Board.objects.filter(Category=Category)
paginator = Paginator(posts, 1)
try: page = int(request.GET.get("page", '1'))
except ValueError: page = 1
try:
posts = paginator.page(page)
except (InvalidPage, EmptyPage):
posts = paginator.page(paginator.num_pages)
return render(request,"boardfinder.html",{"posts":posts,"fo":fo})
return render(request,"boardfinder.html",{"fo":fo})
My models.py
class Board(models.Model):
MATH = 'MATH'
ENGLISH = 'ENGLISH'
SCIENCE = 'SCIENCE'
LANGUAGE = 'LANGUAGE'
CATEGORY = (
(MATH, 'Math'),
(ENGLISH, 'English'),
(SCIENCE, 'Science'),
(LANGUAGE, 'Language'),
)
Category =models.CharField(max_length=30,choices=CATEGORY)
user = models.ForeignKey(User)
name = models.CharField(max_length=100)
created = models.DateTimeField(auto_now_add=True)
picture = models.OneToOneField('Picture',related_name='picture',blank=True,null=True)
def __unicode__(self):
return self.name
class BoardFinderForm(forms.ModelForm):
class Meta:
model = Board
fields = ('Category',)
Important parts of my boardfinder.html
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{{ fo.as_p }}
<input type = "submit" value= "Find WhiteBoard" />
</form>
{% for post in posts.object_list %}
<div class="title">{{ post.name }}</div>
{% endfor %}
<form method="GET">
<p><select name="category">
<option value=""
{% if not request.session.category %}selected{% endif %}>
(All subjects)
</option>
<option value="ENGLISH"
{% if request.session.category == "ENGLISH" %}selected{% endif %}>
English
</option>
<option value="LANGUAGE"
{% if request.session.category == "LANGUAGE" %}selected{% endif %}>
Language
</option>
<option value="MATH"
{% if request.session.category == "MATH" %}selected{% endif %}>
Math
</option>
<option value="SCIENCE"
{% if request.session.category == "SCIENCE" %}selected{% endif %}>
Science
</option>
</select></p>
<input type = "submit" value= "Find WhiteBoard" />
</form>
def BoardFinder(request):
category = request.GET.get('category')
if category:
request.session['category'] = category
posts = Board.objects.filter(Category=category)
paginator = Paginator(posts, 1)
try: page = int(request.GET.get("page", '1'))
except ValueError: page = 1
try:
posts = paginator.page(page)
except (InvalidPage, EmptyPage):
posts = paginator.page(paginator.num_pages)
return render(request,"boardfinder.html",{"posts":posts,"fo":fo})
return render(request,"boardfinder.html",{"fo":fo})