Django for programmers_9

This note is based on Day 21,22 and 23.

  1. class-based-view-overrides

we can create a user order history list on the event-detail.html

open events/templates/events/event_detail.html

<h1>今天吃:{{ event }}。快點餐!</h1>
<table class="table">
<thead>
<tr><th>使用者</th><th>項目</th></tr>
</thead>
<tbody>
{% for order in event.orders.all %}
<tr><td>{{ order.user }}</td><td>{{ order.item }}</td></tr>
{% endfor %}
</tbody>
</table>

Listing on the forms.py

# events/forms.py

from .models import Order

class OrderForm(forms.ModelForm):

class Meta:
model = Order
fields = ['item', 'notes']

def __init__(self, *args, submit_title='Submit', **kwargs):
super().__init__(*args, **kwargs)
self.fields['item'].empty_label = None
self.helper = FormHelper()
self.helper.add_input(Submit('submit', submit_title))

As mentioned earlier (like more than once), Django will automatically foreign key render into HTML select widget. But Django defaults to create an option tag for every possible object (here is all the menu items) and adds an option representing the null value at the top (the default display is like this) The But it is impossible to do nothing at all! So here we use the field attribute of the form to remove the item field (fields will pass back a dict, which contains all the fields in the form), and then set the null value to None, so that Django directly to the option tag Out.

EventDetailView -> get_context_data

# events/views.py

from .forms import OrderForm

class EventDetailView(DetailView):

model = Event

def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
order_form = OrderForm()
data['order_form'] = order_form
return data

queryset attribute is to restrict the select option.

# events/views.py

# ...
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
order_form = OrderForm()
# 注意這行!
order_form.fields['item'].queryset = self.object.store.menu_items.all()
data['order_form'] = order_form
return data
# ...

using post to deal with order

from django.http import HttpResponseBadRequest
from django.shortcuts import redirect

class EventDetailView(LoginRequiredMixin, DetailView):
# ...
def post(self, request, *args, **kwargs):
form = OrderForm(request.POST)
if not form.is_valid():
return HttpResponseBadRequest()
order = form.save(commit=False)
order.user = request.user
order.event = self.get_object()
order.save()
return redirect(order.event.get_absolute_url())

2. formsets

Store object 的 MenuItem model instances

# stores/views.py

from django.forms.models import inlineformset_factory
from .models import MenuItem

def store_update(request, pk):
# ...
MenuItemFormSet = inlineformset_factory(
parent_model=Store, model=MenuItem, fields=('name', 'price',), extra=1,
)
menu_item_formset = MenuItemFormSet(instance=store)
return render(request, 'stores/store_update.html', {
'form': form, 'store': store, 'menu_item_formset': menu_item_formset,
})
{# stores/templates/stores/store_update.html #}

{# ... #}

{% crispy form %}
{% crispy menu_item_formset %} <!-- 新增這一行 -->

menu item formset implements helper

# stores/forms.py

from django.forms.models import inlineformset_factory
from .models import MenuItem

BaseMenuItemFormSet = inlineformset_factory(
parent_model=Store, model=MenuItem, fields=('name', 'price',), extra=1,
)

class MenuItemFormSet(BaseMenuItemFormSet):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = False # 我們要自己包。
self.helper.disable_csrf = True # StoreForm 已經有 CSRF token,不需要重複產生。

3. finishing-touches

we just implement the function

# events/views.py

class EventDetailView(LoginRequiredMixin, DetailView):
# ...
def get_order(self, user):
try:
order = Order.objects.get(user=user, event=self.get_object())
except Order.DoesNotExist:
order = None
return order
# ...
One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.