Two Scoops of Django μ± μμ μκ°ν νΌ ν¨ν΄λ€μ μμ½ μ 리ν΄λ³΄μλ€.
λ§ κ·Έλλ‘ μμ½ν λ΄μ©μ΄κΈ° λλ¬Έμ μ½λμ λν μμΈν μ€λͺ λ€μ μ± μ μ°Έκ³ νμλ κ² μ’λ€ :)
from django.views.generic import CreateView, UpdateView
from braces.views import LoginRequiredMixin
from .models import Flavor
class FlavorCreateView(LoginRequiredMixin, CreateView):
model = Flavor
fields = ('title', 'slug', 'scoops_remaining')
class FlavorUpdateView(LoginRequiredMixin, UpdateView):
model = Flavor
fields = ('title', 'slug', 'scoops_remaining')
Flavor
λͺ¨λΈμ FlavorCreateView
μ FlavorUpdateView
μμ μ΄μ©νλλ‘ νλ€.Flavor
λͺ¨λΈμ κΈ°λ°μ λ ModelForm
μ μλ μμ±νλ€.ModelForm
μ΄ Flavor
λͺ¨λΈμ κΈ°λ³Έ νλ μ ν¨μ± κ²μ¬κΈ°λ₯Ό μ΄μ©νκ² λλ€.from django.core.exceptions import ValidationError
def validate_tasty(value):
if not value.startswith("Tasty"):
msg = "Must start with Tasty"
raise ValidationError(msg)
validate_tasty()
λ₯Ό λ€λ₯Έ μ’
λ₯μ λμ νΈ λͺ¨λΈμ μ μ©νκΈ° μν΄ μ°μ TastyTitleAbstractModel
μ΄λΌλ νλ‘μ νΈ μ λ°μμ μ΄μ©ν μ μλ μΆμν λͺ¨λΈμ μΆκ°νλ€.
Flavor
μ Milkshake
λͺ¨λΈμ΄ κ°κΈ° λ€λ₯Έ λͺ¨λΈμ΄λΌ κ°μ ν λ μ ν¨μ± κ²μ¬κΈ°λ₯Ό νλμ μ±μλ§ μΆκ°νλ κ²μ μ μ νμ§ μμ κ²μ΄λ€.
λ°λΌμ κ·Έ λμ core/models.py
λͺ¨λμ λ§λ€κ³ TastyTitleAbstractModel
μ μ΄κ³³μ μΆκ°νκ² λ€.
from django.db import models
from .validators import validate_tasty
class TastyTitleAbstractModel(models.Model):
title = models.CharField(max_length=255, validators=[validate_tasty])
class Meta:
abstract = True
μμ core/models.py
μ½λμμ λ§μ§λ§ λ μ€μ΄ TastyTitleAbstractModel
μ μΆμν λͺ¨λΈλ‘ λ§λ€μ΄ μ€λ€.
μ΄μ μλ flavors/models.py
μ Flavor
μ½λμμ TastyTitleAbstractModel
μ λΆλͺ¨ ν΄λμ€λ‘ μ§μ ν΄ λ³΄κ² λ€.
from django.core.urlresolvers import reverse
from django.db import models
from core.models import TastyTitleAbstractModel
class Flavor(TastyTitleAbstractModel):
slug = models.SlugField()
scoops_remaining = models.IntegerField(default=0)
def get_absolute_url(self):
return reverse("flavors:detail", kwargs={"slug": self.slug})
validate_tasty()
λ₯Ό μ΄μ©νκ³ μ ν λλ μ΄λ»κ² ν΄μΌ ν κΉ?μ΄λ¬ν κ²½μ°λ€μ μ²λ¦¬νκΈ° μν΄ μ»€μ€ν
νλ μ ν¨μ± κ²μ¬κΈ°λ₯Ό μ΄μ©νλ 컀μ€ν
FlavorForm
μ μμ±νκΈ°λ‘ νλ€.
from django import forms
from core.validators import validate_tasty
from .models import Flavor
class FlavorForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(FlavorForm, self).__init__(*args, **kwargs):
self.fields["title"].validators.append(validate_tasty)
self.fields["slug"].validators.append(validate_tasty)
class Meta:
model = Flavor
μ₯κ³ μ λͺ¨λΈ κΈ°λ° μμ λ·°λ λ·°μ λͺ¨λΈ μμ±μ κΈ°λ°μΌλ‘ λͺ¨λΈνΌμ μλμΌλ‘ μμ±ν΄ μ€λ€.
from django.contrib import messages
from django.views.generic import CreateView, UpdateView, DetailView
from braces.views import LoginRequiredMixin
from .models import Flavor
from .forms import FlavorForm
class FlavorActionMixin(object):
model = Flavor
fields = ('title', 'slug', 'scoops_remaining')
@property
def success_msg(self):
return NotImplemented
def form_valid(self, form):
messages.info(self.request, self.success_msg)
return super(FlavorActionMixin, self).form_valid(form)
class FlavorCreateView(LoginRequiredMixin, FlavorActionMixin, CreateView):
success_msg = "created"
form_class = FlavorForm
class FlavorUpdateView(LoginRequiredMixin, FlavorActionMixin, UpdateView):
success_msg = "updated"
form_class = FlavorForm
class FlavorDetailView(DetailView):
model = Flavor
μ λ κ°μ§ κ²½μ° μ λΆ μ»€μ€ν
λ‘μ§μΌλ‘ clean()
λλ clean_<field_name>()
λ©μλλ₯Ό μ€λ²λΌμ΄λ© ν μ μλ μ΅μ μ κ²½μ°λ€.
κΈ°λ³Έ λλ 컀μ€ν
νλ μ ν¨μ± κ²μ¬κΈ°κ° μ€νλ ν, μ₯κ³ λ λ€μ κ³Όμ μΌλ‘ clean()
λ©μλλ clean_<field_name>()
λ©μλλ₯Ό μ΄μ©νμ¬ μ
λ ₯λ λ°μ΄ν°μ μ ν¨μ±μ κ²μ¬νλ μ μ°¨λ₯Ό μ§ννλ€.
clean()
λ©μλλ μ΄λ€ νΉλ³ν νλμ λν μ μλ κ°μ§κ³ μμ§ μκΈ° λλ¬Έμ λ κ° λλ κ·Έ μ΄μμ νλλ€μ λν΄ μλ‘ κ°μ μ ν¨μ±μ κ²μ¬νλ 곡κ°μ΄ λλ€.clean
) μ ν¨μ± κ²μ¬ μνλ μμ λ°μ΄ν°μ λν΄ μ ν¨μ±μ κ²μ¬νκΈ°μ μ’μ μ₯μλ€. μ΄λ―Έ μ ν¨μ± κ²μ¬λ₯Ό μΌλΆ λ§μΉ λ°μ΄ν°μ λν΄ λΆνμν λ°μ΄ν°λ² μ΄μ€ μ°λμ μ€μΌ μ μλ€.from django import forms
from flavors.models import Flavor
class IceCreamOrderForm(forms.Form):
slug = forms.ChoiceField("Flavor")
toppings = forms.CharField()
def __init__(self, *args, **kwargs):
super(IceCreamOrderForm, self).__init__(*args, **kwargs)
self.fields["slug"].choices = [(x.slug, x.title) for x in Flavor.objects.all()]
def clean_slug(self):
slug = self.cleaned_data["slug"]
if Flavor.objects.get(slug=slug).scoops_remaining <= 0:
raise forms.ValidationError(msg)
return msg
def clean(self):
cleaned_data = super(IceCreamOrderForm, self).clean()
slug = cleanec_data.get("slug", "")
toppings = cleaned_data.get("toppings", "")
if "chocolate" in slug.lower() and "chocolate" in toppings.lower():
msg = "Your order has too much chocolate."
raise forms.ValidationError(msg)
return cleaned_data
from django.core.urlresolvers import reverse
from django.db import models
class IceCreamStore(models.Model):
title = modes.CharField(max_length=100)
block_address = models.TextField()
phone = models.CharField(max_length=20, blank=True)
description = models.TextField(blank=True)
def get_absolute_url(self):
return reverse("store_detail", kwargs={"pk": self.pk})
μ¬μ©μκ° title
κ³Ό block_address
λ μ
λ ₯ν΄μΌ νμ§λ§ phone
κ³Ό description
νλλ μ
λ ₯νμ§ μμλ λκ² κ΅¬μ±λμ΄ μλ€.
νμ μ¬μ©μκ° phone
κ³Ό description
νλλ₯Ό μΆκ°μ μΌλ‘ μ
λ°μ΄νΈνλ κ²μ΄ κ°λ₯νλλ‘ κ΅¬μ±νκ³ μΆλ€λ©΄ μ΄λ»κ² ν΄μΌν κΉ?
μ₯κ³ νΌμ μ¬μ©ν λ λ°λμ λ€μ μ¬νμ κΈ°μ΅νμ.
μ€μ²΄νλ νΌ κ°μ²΄λ μ μ¬ λμ
λ리 κ°μ²΄μΈ fields
μμ± μμ κ·Έ νλλ€μ μ μ₯νλ€.
λ°λΌμ νΌμΌλ‘ νλμ μ μλ₯Ό 볡μ¬, λΆμ΄κΈ° νλ λμ μ κ°λ¨νκ² ModelForm
μ __init__()
λ©μλμμ μλ‘μ΄ μμ±μ μ μ©νλ©΄ λλ€.
from .models import IceCreamStore
class IceCreamStoreUpdateForm(forms.ModelForm):
class Meta:
model = IceCreamStore
def __init__(self, *args, **kwargs):
super(IceCreamStoreUpdateForm, self).__init__(*args, **kwargs)
self.fields["phone"].required = True
self.fields["description"].required = True
κ²°κ΅ κΈ°μ΅ν΄μΌ ν μ€μν μ μ μ₯κ³ μ νΌλ κ²°κ΅ νμ΄μ¬ ν΄λμ€λΌλ μ¬μ€μ΄λ€. μ₯κ³ μ νΌ λν κ°μ²΄λ‘ μ€μ²΄νλκ³ μνΌν΄λμ€κ° λμ΄ λ€λ₯Έ ν΄λμ€λ₯Ό μμνκΈ°λ νλ€.
from django import forms
from .models import IceCreamStore
class IceCreamStoreCreateForm(forms.ModelForm):
class Meta:
model = IceCreamStore
fields = ("title", "block_address", )
class IceCreamStoreUpdateForm(IceCreamStoreCreateForm):
def __init__(self, *args, **kwargs):
super(IceCreamStoreUpdateForm, self).__init__(*args, **kwargs)
self.fields["phone"].requird = True
self.fields["description"].required = True
class Meta(IceCreamStoreCreateForm.Meta):
fields = ("title", "block_address", "phone", "description", )
from django.views.generic import CreateView, UpdateView
from .forms import IceCreamStoreCreateForm
from .forms import IceCreamStoreUpdateForm
from .models import IceCreamStore
class IceCreamCreateView(CreateView):
model = IceCreamStore
form_class = IceCreamStoreCreateForm
class IceCreamUpdateView(UpdateView):
model = IceCreamStore
form_class = IceCreamStoreUpdateForm
class TitleSearchMixin(object):
def get_queryset(self):
queryset = super(TitleSearchMixin, self).get_queryset()
q = self.request.GET.get("q")
if q:
return queryset.filter(title__icontains=q)
return queryset
from django.views.generic import ListView
from core.views import TitleSearchMixin
from .models import Flavor
class FlavorListView(TitleSearchMixin, ListView):
model = Flavor
from django.views import ListView
from core.views import TitleSearchMixin
from .models import Store
class IceCreamStoreListView(TitleSearchMixin, ListView):
model = Store
<form action="" method="GET">
<input type="text" name="q" />
<button type="submit">search</button>
</form>