μ§λ ν¬μ€ν μ μ΄μ΄ μ΄λ²μλ Two Scoops of Django μ± μ μ₯κ³ νΌμ λν λΆλΆμ μμ½ μ 리ν΄λ³΄μλ€.
μ₯κ³ νΌμ μ λλ‘ μ΄μ©νλ©΄ κ·Έλμ λ·°μ κ±°μΆμ₯μ€λ½κ² λμ΄λμλ μ ν¨μ± κ²μ¬μ λν λΆλΆμ μλ΅ν μ μλ€. μ λ§ κ°λ ₯ν κΈ°λ₯μ΄λ μμ°¨κ² μ¨λ¨Ήλλ‘ νμ!
μ₯κ³ νΌμ νμ΄μ¬ λμ λ리μ μ ν¨μ±μ κ²μ¬νλ λ° μ΅μμ λꡬλ€. λλΆλΆμ κ²½μ° POSTκ° ν¬ν¨λ HTTP μμ²μ λ°μ μ ν¨μ±μ κ²μ¬νλ λ° μ΄μ©νμ§λ§ μ΄λ° κ²½μ° μΈμλ μ λλ‘ μ°μ§ λ§λΌλ μ μ½μ μλ€.
λ€λ₯Έ νλ‘μ νΈλ‘λΆν° CSV νμΌμ λ°μ λͺ¨λΈμ μ λ°μ΄νΈνλ μ₯κ³ μ±μ κ°μ§κ³ μλ€κ³ νμ.
import csv
import StringIO
from .models import Purchase
def add_csv_purchases(rows):
rows = StringIO.StringIO(rows)
records_added = 0
for row in csv.DictReader(rows, delimiter=','):
purchase.objects.create(**row)
records_added += 1
return records_added
μ΄ μ½λμμ κ°κ³Όνκ³ μλ μ μ Purchase
λͺ¨λΈμμ λ¬Έμμ΄ κ°μΌλ‘ μ μ₯λμ΄ μλ μ
λ¬κ° μ€μ λ‘ μ‘΄μ¬νλ μ
λ¬μΈμ§ κ·Έ μ ν¨μ±μ κ²μ¬νκ³ μμ§ μλ€λ μ μ΄λ€.
λ¬Όλ‘ add_csv_purchases()
ν¨μμ μ ν¨μ± κ²μ¬ μ½λλ₯Ό μΆκ°ν μλ μκ² μ§λ§ λ§€λ² λ°μ΄ν°κ° λ°λ λλ§λ€ 볡μ‘ν μ ν¨μ± κ²μ¬ μ½λλ₯Ό νμμ λ§μΆ° μ μ§ κ΄λ¦¬νκΈ°λ λ§€μ° λ²κ±°λ‘μ΄ μΌμ΄λ€.
μ₯κ³ μ λͺ¨λΈ νΌμ μ΄μ©νλ©΄ λ€μκ³Ό κ°μ΄ μ λ ₯ λ°μ΄ν°μ λν΄ κ°λ¨νκ² μ ν¨μ± κ²μ¬λ₯Ό ν μ μλ€.
import csv
import StringIO
from django import forms
from .models import Purchase, Seller
class PurchaseForm(forms.ModelForm):
class Meta:
model = Purchase
def clean_seller(self):
seller = self.cleaned_data["seller"]
try:
Seller.objects.get(name=seller)
except Seller.DoesNotExist:
msg = f"{seller} does not exist in purchase #{self.cleaned_data['purchase_number']}."
raise forms.ValidationError(msg)
return seller
def add_csv_purchase(rows):
rows = StringIO.StringIO(rows)
records_added = 0
errors = []
for row in csv.DictReader(rows, delimiter=','):
from = PurchaseForm(row)
if form.is_valid():
form.save()
records_added += 1
else:
errors.append(form.errors)
return records_added, errors
λ°μ΄ν°λ₯Ό λ³κ²½νλ λͺ¨λ HTML νΌμ POST λ©μλλ₯Ό μ΄μ©νμ¬ λ°μ΄ν°λ₯Ό μ μ‘νκ² λλ€.
<form action="{% url 'flavor_add' %}" method="POST">
μ₯κ³ μλ CSRF(Cross-Site Request Forgery protection, μ¬μ΄νΈ κ° μμ‘° μμ² λ°©μ§)κ° λ΄μ₯λμ΄ μλ€.
CSRF 보μμ μ μ κΊΌ λμ΄λ λλ κ²½μ°λ‘λ λ¨Έμ λ€ μ¬μ΄μ μ΄μ©λλ API μ¬μ΄νΈλ₯Ό μ μν λλ€.
django-tastypie
λ django-rest-framework
κ°μ API νλ μμν¬μμλ μ΄λ¬ν μ²λ¦¬λ₯Ό μλμΌλ‘ λ€ν΄μ€λ€.
API μμ²μ λ¨μΌ μμ²μ κΈ°λ°μΌλ‘ μΈμ¦ μμ²/μΈμ¦ νμ©μ νκΈ° λλ¬Έμ μ΄λ° κ²½μ° μΌλ°μ μΌλ‘ HTTP μΏ ν€λ₯Ό μΈμ¦ μλ¨μΌλ‘ μ΄μ©νμ§ μλλ€.
μ₯κ³ μ CsrfViewMiddleware
λ₯Ό μ¬μ΄νΈ μ 체μ λν 보νΈλ§μΌλ‘ μ΄μ©ν¨μΌλ‘μ¨ μΌμΌμ΄ μμΌλ‘ csrf_protect
λ₯Ό λ·°μ λ°μ½λ μ΄ν
νμ§ μμλ λλ€.
AJAXλ₯Ό ν΅ν΄ λ°μ΄ν°λ₯Ό μΆκ°ν λλ λ°λμ μ₯κ³ μ CSRF 보μμ μ΄μ©ν΄μΌ νλ€. μ λ AJAX λ·°λ₯Ό CSRFμ μμΈ μ²λ¦¬νμ§ λ§κΈ° λ°λλ€. λμ μ HTTP ν€λμ X-CSRFTokenμ μ€μ ν΄λλλ‘ νλ€.
λλλ‘ μ₯κ³ νΌμ clean()
, clean_FOO()
, save()
λ©μλμ μΆκ°λ‘ νΌ μΈμ€ν΄μ€ μμ±μ΄ νμν λκ° μλ€.
μ΄λ΄ κ²½μ°μλ request.user
κ°μ²΄λ₯Ό μ΄μ©νλ©΄ λλ€.
from django import forms
from .models import Taster
class TasterForm(forms.ModelForm):
class Meta:
model = Taster
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user')
super(TasterForm, self).__init__(*args, **kwargs)
from django.views.generic import UpdateView
from braces.views import LoginRequiredMixin
from .forms import TasterForm
from .models import Taster
class TasterUpdateView(LoginRequiredMixin, UpdateView):
model = Taster
form_class = TasterForm
success_url = "/someplace/"
def get_form_kwargs(self):
kwargs = super(TasterUpdateView, self).get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
form.is_valid()
κ° νΈμΆλ λ μ¬λ¬ κ°μ§ μΌμ΄ λ€μ μμλ‘ μ§νλλ€.
form.is_valid()
λ form.full_clean()
λ©μλλ₯Ό νΈμΆνλ€.form._full_clean()
μ νΌ νλλ€κ³Ό κ°κ°μ νλ μ ν¨μ±μ νλνλ κ²μ¬νλ©΄μ λ€μκ³Ό κ°μ κ³Όμ μ μννλ€.
to_python()
μ μ΄μ©νμ¬ νμ΄μ¬ νμμΌλ‘ λ³ννκ±°λ λ³νν λ λ¬Έμ κ° μκΈ°λ©΄ ValidationError
λ₯Ό μΌμΌν¨λ€.ValidationError
λ₯Ό μΌμΌν¨λ€.clean_<field>()
λ©μλκ° μμΌλ©΄ μ΄λ₯Ό μ€ννλ€.form.full_clean()
μ΄ form.clean()
λ©μλλ₯Ό μ€ννλ€.ModelForm
μΈμ€ν΄μ€μ κ²½μ° form.post_clean()
μ΄ λ€μ μμ
μ νλ€.
form.is_valid()
κ° True
λ False
λ‘ μ€μ λμ΄ μλ κ²κ³Ό κ΄κ³μμ΄ ModelForm
λ°μ΄ν°λ₯Ό λͺ¨λΈ μΈμ€ν΄μ€λ‘ μ€μ νλ€.clean()
λ©μλλ₯Ό νΈμΆνλ€. μ°Έκ³ λ‘ ORMμ ν΅ν΄ λͺ¨λΈ μΈμ€ν΄μ€λ₯Ό μ μ₯ν λλ λͺ¨λΈμ clean()
λ©μλκ° νΈμΆλμ§λ μλλ€.ModelForm
μμ νΌ λ°μ΄ν°λ λ κ°μ§ κ°κΈ° λ€λ₯Έ λ¨κ³λ₯Ό ν΅ν΄ μ μ₯λλ€.
form.save()
λ©μλμ μν΄ μ μ©λκΈ° μ κΉμ§λ ModelForm
μ΄ λͺ¨λΈ μΈμ€ν΄μ€λ‘ μ μ₯λμ§ μκΈ° λλ¬Έμ μ΄λ κ² λΆλ¦¬λ κ³Όμ μ체λ₯Ό μ₯μ μΌλ‘ μ΄μ©ν μ μλ€.
μλ₯Ό λ€λ©΄ νΌ μ λ ₯ μλ μ€ν¨μ λν΄ μ’ λ μμΈν μ¬νμ΄ νμν λ, μ¬μ©μκ° μ λ ₯ν νΌμ λ°μ΄ν°μ λͺ¨λΈ μΈμ€ν΄μ€μ λ³νλ₯Ό λ λ€ μ μ₯ν μ μλ€.
from django.db import models
class ModelFormFailuserHistory(models.Model):
form_data = models.TextField()
model_data = models.TextField()
import json
from django.contrib import messages
from django.cors import serializers
from core.models import ModelFormFailuerHistory
class FlavorActionMixin(self):
@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)
def form_invalid(self, form):
form_data = json.dumps(form.cleaned_data)
model_data = serializers.seralize("json", [form.instance])[1:-1]
ModelFormFailuserHistory.objects.create(
form_data=form_data,
model_data=model_data
)
return super(FlavorActionMixin, self).form_invalid(form)
from django import forms
class IceCreamReviewForm(forms.Form):
# tester νΌμ λλ¨Έμ§ λΆλΆ
def clean(self):
cleaned_data = super(TasterForm, self).clean()
flavor = cleaned_data.get("flavor")
age = cleaned_data.get("age")
if flavor == 'coffee' and age < 3:
msg = 'Coffee Ice Cream is not for Babies.'
self.add_error('flavor', msg)
self.add_error('age', msg)
return cleaned_data