oldsport oldsport - 29 days ago 8
Python Question

Django - How to pass queryset to the template using a class based UpdateView?

Is there a way to pass a queryset into the fields collection (shown in the views.py)? My intend is to populate a select HTML element with only active members. Or is there any other way or best practice to solve this?

views.py:

class Orders_update(UpdateView):
model = Order
active_members = Member.objects.all().filter(active=True)
fields = ['order_text', 'customer', 'active_members']
template_name = "orders/orders_update.html"
success_url = '../../../orders'


orders_update.html:

{% extends "base.html" %}
{% load widget_tweaks %}
{% block content %}
<h1>Order - Update</h1>
<form class="form-group" style="width:200px" action="" method="post">{% csrf_token %}
<div class="form-group">
<div class="form-group">
{{ form.order_text|add_class:"form-control" }}
{{ form.customer|add_class:"form-control" }}
{{ form.active_members|add_class:"form-control" }}
<input class="form-control" type="submit" value="Update" />
</div>
</div>
</form>
{% endblock %}


models.py:

from django.db import models
from django.utils import timezone


class Order(models.Model):
customer = models.ForeignKey('Customer')
member = models.ForeignKey('Member')
order_text = models.CharField(max_length=200)
pick_up = models.CharField(max_length=200)
destination = models.CharField(max_length=200)
created_date = models.DateTimeField(default=timezone.now)
changed_date = models.DateTimeField(blank=True, null=True)

def created(self):
self.changed_date = timezone.now()
self.save()

def __str__(self):
return self.order_text


class Customer(models.Model):
company_name = models.CharField(max_length=200)
created_date = models.DateTimeField(default=timezone.now)
changed_date = models.DateTimeField(blank=True, null=True)

def created(self):
self.changed_date = timezone.now()
self.save()

def __str__(self): return self.company_name


class Member(models.Model):
lastname = models.CharField(max_length=20)
firstname = models.CharField(max_length=20)
created_date = models.DateTimeField(default=timezone.now)
changed_date = models.DateTimeField(blank=True, null=True)
active = models.BooleanField(default=True)

def created(self):
self.changed_date = timezone.now()
self.save()

def __str__(self):
return self.lastname

Answer

It sounds like you want to restrict options for the Order.members field to those with active=True.

You can do this by creating a model form, and changing the queryset in the __init__ method.

from django import forms

class OrderForm(forms.ModelForm):
    class Meta:
        fields = ['order_text', 'customer', 'members']

    def __init__(self, *args, **kwargs):
        super(OrderForm, self).__init__(*args, **kwargs)
        self.fields['members'].queryset = Member.objects.all().filter(active=True)

Then update your view to use your model form by setting form_class.

class Orders_update(UpdateView):
    model = Order
    form_class = OrderForm
    ...

Finally, the field is call member, so you should change the template to:

{{ form.member|add_class:"form-control" }}

As an aside, it's not recommended to use a relative url like '../../../orders'. It would be better to use reverse_lazy. For example, if the name of the URL pattern is orders, then you can use:

success_url = reverse_lazy('orders')
Comments