項目技術重難點分析:html
模型層:模型是您的數據惟一併且準確的信息來源。它包含您正在儲存的數據的重要字段和行爲。通常來講,每個模型都映射一個數據庫表。python
每各模型都是一個python的類,這些類繼承 django.db.models.Model
mysql
模型類的每一個屬性都至關於一個數據庫的字段 linux
from django.db import models class Person(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) ### 會映射成下面的 sql CREATE TABLE myapp_person ( "id" serial NOT NULL PRIMARY KEY, "first_name" varchar(30) NOT NULL, "last_name" varchar(30) NOT NULL );
myapp_person的名字由 app名稱_class名稱,id字段是一個默認字段,若是有主鍵則被覆蓋,默認是不容許爲空。git
模型的使用:在setting中的INSTALLED_APPS中註冊你的app名稱,正則表達式
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'courses', # 如下4個是註冊的app名稱 'organization', 'users', 'captcha', ]
python manage.py makemigrations:至關於在該app下創建 migrations目錄,並記錄下你全部的關於modes.py的改動,好比0001_initial.py, 可是這個改動尚未做用到數據庫文件sql
python manager.py migrate:將該改動做用到數據庫文件,好比產生table,修改字段的類型等。數據庫
字段:django
字段名備註:除了 ForeignKey
,ManyToManyField
和OneToOneField
,任何字段類型都接收一個可選的參數verbose_name
,若是未指定該參數值,Django會自動使用該字段的屬性名做爲該參數值,而且把下劃線轉換爲空格。後端
null:字段是否爲空,默認爲False
blank:字段是否爲空,默認爲False(該注意選項對話與False
不一樣,null
選項僅僅是數據庫層面的設置,然而blank
是涉及表單驗證方面。若是一個字段設置爲blank=True
,在進行表單驗證時,接收的數據該字段值容許爲空,而爲設置blank=False
時,不容許爲空。)
choices:該參數接收一個可迭代的列表或元組(基本單位爲二元組)。若是指定了該參數,在實例化該模型時,該字段只能取選項列表中的值。每一個二元組的第一個值會儲存在數據庫中,而第二個值將只會用於顯示做用。
from django.db import models class Person(models.Model): SHIRT_SIZES = ( ('S', 'Small'), ('M', 'Medium'), ('L', 'Large'), ) name = models.CharField(max_length=60) shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
default:該字段的默認值。能夠是一個值或者是個可調用的對象,若是是個可調用對象,每次實例化模型時都會調用該對象。
help_text:使用表單小部件顯示的額外「幫助」文本。即便您的字段未在表單上使用,它也對文檔頗有用。
primary_key:若是設置爲True
,將該字段設置爲該模型的主鍵。
unique:若是設置爲True
,這個字段必須在整個表中保持值惟一。
多對一關係:使用 django.db.models.Model.ForeignKey 類,
# Car與Manufacturer 是多對一的關係,on_delete = models.CASCADE 是聯級刪除 from django.db import models class Manufacturer(models.Model): # ... pass class Car(models.Model): manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE) # ...
多對多關係:建一個多對多的表,顯示的寫出多對多表時能夠記錄除2的外鍵的字段。
from django.db import models class Person(models.Model): name = models.CharField(max_length=128) def __str__(self): return self.name class Group(models.Model): name = models.CharField(max_length=128) members = models.ManyToManyField(Person, through='Membership') def __str__(self): return self.name class Membership(models.Model): person = models.ForeignKey(Person, on_delete=models.CASCADE) group = models.ForeignKey(Group, on_delete=models.CASCADE) date_joined = models.DateField() invite_reason = models.CharField(max_length=64)
Meta選項:給模型賦予元型態數據,模型的元數據是指「全部不是字段的東西」,好比排序選項,數據庫表名,abstract = True表示這個模型是一個抽象基類。
from django.db import models class Ox(models.Model): horn_length = models.IntegerField() class Meta: ordering = ["horn_length"] verbose_name_plural = "oxen"
繼承模式:1. 抽象基類 2. 多表繼承 3. 代理模型
抽象基類:當您想要將一些公共信息放入許多其餘模型時,抽象基類很是有用。你寫你的基類,並把abstract=True
在元類。而後,此模型不會用於建立任何數據庫表。相反,當它用做其餘模型的基類時,其字段將添加到子類的字段中。(基類不會產生數據表,只會共享數據,而子類產生數據表)
from django.db import models class CommonInfo(models.Model): name = models.CharField(max_length=100) age = models.PositiveIntegerField() class Meta: abstract = True class Student(CommonInfo): home_group = models.CharField(max_length=5)
Meta繼承:若是子類沒有Meta方法則會繼承父類的Meta,也能夠擴展父類的Meta方法。
from django.db import models class CommonInfo(models.Model): # ... class Meta: abstract = True ordering = ['name'] class Student(CommonInfo): # ... class Meta(CommonInfo.Meta): db_table = 'student_info'
多表繼承:每一個模型對應於本身的數據庫表,子類不會繼承父類的Meta
from django.db import models class Place(models.Model): name = models.CharField(max_length=50) address = models.CharField(max_length=80) class Restaurant(Place): serves_hot_dogs = models.BooleanField(default=False) serves_pizza = models.BooleanField(default=False)
代理繼承:你可能只想更改 model 在 Python 層的行爲實現。子類和父類共同操做同一個表。
from django.db import models class Person(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=30) class MyPerson(Person): class Meta: proxy = True def do_something(self): # ... pass
ORM:提供數據操做的API
######### 如下是例子中用到的模型樣例
from django.db import models class Blog(models.Model): name = models.CharField(max_length=100) tagline = models.TextField() def __str__(self): return self.name class Author(models.Model): name = models.CharField(max_length=200) email = models.EmailField() def __str__(self): return self.name class Entry(models.Model): blog = models.ForeignKey(Blog, on_delete=models.CASCADE) headline = models.CharField(max_length=255) body_text = models.TextField() pub_date = models.DateField() mod_date = models.DateField() authors = models.ManyToManyField(Author) n_comments = models.IntegerField() n_pingbacks = models.IntegerField() rating = models.IntegerField() def __str__(self): return self.headline
建立並保存對象:
# 在執行 save 時不會執行數據操做
from blog.models import Blog b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.') b.save() # insert 操做 b5.name = 'New name' # b5是Blog的實例 b5.save() # 執行update操做
保存ForeignKey、MarytoMaryFiled字段:
更新ForeignKey字段的工做方式與保存普通字段的方式徹底相同。
from blog.models import Blog, Entry entry = Entry.objects.get(pk=1) cheese_blog = Blog.objects.get(name="Cheddar Talk") entry.blog = cheese_blog entry.save()
更新MarytoMaryFiled的方式是使用add方法:
# 只增長一條數據 from blog.models import Author joe = Author.objects.create(name="Joe") entry.authors.add(joe) # 增長多條數據 john = Author.objects.create(name="John") paul = Author.objects.create(name="Paul") george = Author.objects.create(name="George") ringo = Author.objects.create(name="Ringo") entry.authors.add(john, paul, george, ringo)
select:(QuerySet對象是相互獨立的,能夠嵌套 filter 等)
all_entries = Entry.objects.all() # 得到Entry全部對象,返回包含QuerySet對象的列表 # filter是返回包含給定條件的QuerySet對象 # exclude是返回不包含給定條件的QuerySet對象 Entry.objects.filter(pub_date__year=2006) Entry.objects.all().filter(pub_date__year=2006) Entry.objects.filter( headline__startswith='What').exclude(pub_date__gte=datetime.date.today()).filter(pub_date__gte=datetime.date(2005, 1, 30)) # get 方法與 filter 相似,可是隻返回一個數據,若是有0或多條數據則會報錯 one_entry = Entry.objects.get(pk=1) # limit 操做 Entry.objects.all()[:5]
跨表查找:
Entry.objects.filter(blog__name='Beatles Blog') # 正向查詢 Blog.objects.filter(entry__headline__contains='Lennon') # 反向查詢,使用模型的小寫名稱__字段名 Blog.objects.filter(entry__authors__name='Lennon') Blog.objects.filter(entry__authors__name__isnull=True) Blog.objects.filter(entry__authors__isnull=False, entry__authors__name__isnull=True) # F查詢,F查詢專門對對象中某列值的操做 from django.db.models import F Entry.objects.filter(n_comments__gt=F('n_pingbacks')) Entry.objects.filter(n_comments__gt=F('n_pingbacks') * 2) Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks')) Entry.objects.filter(authors__name=F('blog__name')) # 使用雙下劃線表示法來跨越F()對象中的關係 Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3)) Entry.objects.filter(headline__contains='%') # 包含關係,相似like # Q查詢,Q查詢能夠組合使用 「&」, 「|」 操做符,當一個操做符是用於兩個Q的對象,它產生一個新的Q對象, # Q對象能夠用 「~」 操做符放在前面表示否認,也可容許否認與不否認形式的組合。Q對象能夠與關鍵字參數查詢一塊兒使用, #不過必定要把Q對象放在關鍵字參數查詢的前面。 Poll.objects.get(Q(question__startswith='Who'),Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))) SELECT * from polls WHERE question LIKE 'Who%'AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06') Poll.objects.get(Q(pub_date=date(2005, 5, 2)) |Q(pub_date=date(2005, 5, 6)),question__startswith='Who')
# 刪除,每個QuerySet都有delete方法,返回已刪除的對象數和具備每一個對象類型的刪除數的字典 Entry.objects.filter(pub_date__year=2005).delete() Entry.objects.all().delete() # 更新操做 Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same') b = Blog.objects.get(pk=1) Entry.objects.all().update(blog=b) Entry.objects.select_related().filter(blog=b).update(headline='Everything is the same') Entry.objects.all().update(n_pingbacks=F('n_pingbacks') + 1) Entry.objects.update(headline=F('blog__name'))
聚合函數:
from django.db import models class Author(models.Model): name = models.CharField(max_length=100) age = models.IntegerField() class Publisher(models.Model): name = models.CharField(max_length=300) class Book(models.Model): name = models.CharField(max_length=300) pages = models.IntegerField() price = models.DecimalField(max_digits=10, decimal_places=2) rating = models.FloatField() authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE) pubdate = models.DateField() class Store(models.Model): name = models.CharField(max_length=300) books = models.ManyToManyField(Book)
aggregate:aggregate是一個終結子句QuerySet
,當被調用時,返回一個key-value的字典。名稱是聚合值的標識符; 該值是計算的聚合。名稱將根據字段名稱和聚合函數自動生成。若是要手動指定聚合值的名稱,能夠經過在指定聚合子句時提供該名稱來執行此操做:
>>> from django.db.models import Avg >>> Book.objects.aggregate(Avg('price')) {'price__avg': 34.35} >>> Book.objects.aggregate(average_price=Avg('price')) {'average_price': 34.35} >>> from django.db.models import Avg, Max, Min >>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price')) {'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}
annotate:返回的是QuerySet對象,只不過多了聚合函數字段,能夠與任何 filter、order_by等連用。
# Build an annotated queryset >>> from django.db.models import Count >>> q = Book.objects.annotate(Count('authors')) # Interrogate the first object in the queryset >>> q[0] <Book: The Definitive Guide to Django> >>> q[0].authors__count 2
其餘:
# order by Book.objects.annotate(num_authors=Count('authors')).order_by('num_authors') # 加 - 是DESC # values,查找那列 Author.objects.values('name').annotate(average_rating=Avg('book__rating'))
Manager(提供數據庫查詢操做的接口):默認狀況下,每一個模型都會默認建立一個objects屬性,你也能夠顯示的寫在模型內、重命名、增長、修改objects。
from django.db import models class Person(models.Model): #... people = models.Manager()
# First, define the Manager subclass. class DahlBookManager(models.Manager): def get_queryset(self): return super().get_queryset().filter(author='Roald Dahl') # Then hook it into the Book model explicitly. class Book(models.Model): title = models.CharField(max_length=100) author = models.CharField(max_length=50) objects = models.Manager() # The default manager. dahl_objects = DahlBookManager() # The Dahl-specific manager.
原生sql的查詢:
class Person(models.Model): first_name = models.CharField(...) last_name = models.CharField(...) birth_date = models.DateField(...) >>> for p in Person.objects.raw('SELECT * FROM myapp_person'): ... print(p) John Smith Jane Jones >>> first_person = Person.objects.raw('SELECT * FROM myapp_person')[0]
數據庫的設置:default必須存在
不一樣app使用不一樣數據庫
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), }, 'db1': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'django_advanced', 'HOST': '127.0.0.1', 'PORT': '3306', 'USER': 'root', 'PASSWORD': 'root', } }
設置setting:
DATABASES_APPS_MAPPING = { 'polls': 'default', 'polls2': 'db1', } DATABASE_ROUTERS = ['my_blog.database_app_router.DatabaseAppsRouter'] # 路由文件的路徑
路由文件:database_app_router.py
from django.conf import settings class DatabaseAppsRouter(object): def db_for_read(self, model, **hints): app_label = model._meta.app_label # 取Meta下的app_label(app名稱)屬性 if app_label in settings.DATABASES_APPS_MAPPING: res = settings.DATABASES_APPS_MAPPING[app_label] print(res) return res return None def db_for_write(self, model, **hints): app_label = model._meta.app_label if app_label in settings.DATABASES_APPS_MAPPING: return settings.DATABASES_APPS_MAPPING[app_label] return None def allow_relation(self, obj1, obj2, **hints): # 獲取對應數據庫的名字 db_obj1 = settings.DATABASES_APPS_MAPPING.get(obj1._mata.app_label) db_obj2 = settings.DATABASES_APPS_MAPPING.get(obj2._mata.app_label) if db_obj1 and db_obj2: if db_obj1 == db_obj2: return True else: return False return None def db_for_migrate(self, db, app_label, model_name=None, **hints): if db in settings.DATABASES_APPS_MAPPING.values(): return settings.DATABASES_APPS_MAPPING.get(app_label) == db elif app_label in settings.DATABASES_APPS_MAPPING: return False return None
同一個app下使用不一樣的數據庫:
class Book2(models.Model): author = models.CharField(max_length=1024, blank=True, null=True) title = models.CharField(max_length=1024) class Meta: app_label = 'polls2'
URL調度:URL到視圖函數的映射(查找不包括域名)
1.若是傳入的對象HttpRequest具備root_urlconf(setting值的變量)屬性,則有這個值代替root_urlconf
2.查找urlpatterns變量,其中有django.urls.path()和
django.urls.re_path()
3.jango依次匹配每一個URL模式,在與請求的URL匹配的第一個模式停下來
4.一旦其中一個URL模式匹配,Django就會導入並調用給定的視圖,並傳參數:HttpRequest實例、若是匹配的URL模式未返回任何命名組,則正則表達式中的匹配將做爲位置參數提供、鍵字參數由路徑表達式匹配的任何命名部分組成,由或者 可選kwargs
參數中指定的任何參數覆蓋
from django.urls import path from . import views urlpatterns = [ path('articles/2003/', views.special_case_2003), path('articles/<int:year>/', views.year_archive), path('articles/<int:year>/<int:month>/', views.month_archive), path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail), ]
str:匹配除路徑分隔符以外的任何非空字符串'/'
。若是轉換器未包含在表達式中,則這是默認值。
int:匹配零或任何正整數。返回一個int。
slug: 匹配由ASCII字母或數字組成的任何slug字符串,以及連字符和下劃線字符。
uuid: 匹配格式化的UUID。要防止多個URL映射到同一頁面,必須包含短劃線,而且字母必須爲小寫。
path:匹配任何非空字符串,包括路徑分隔符 '/'
。
正則表達式:django.urls.re_path(),(?P<name>pattern)
,name
的名稱,而且 pattern
是要匹配的模式。
from django.urls import path, re_path from . import views urlpatterns = [ path('articles/2003/', views.special_case_2003), re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive), re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive), re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<slug>[\w-]+)/$', views.article_detail), ]
包含其餘app的路由:它都會刪除與該點匹配的URL的任何部分,並將剩餘的字符串發送到包含的URLconf以進行進一步處理。
from django.urls import include, path urlpatterns = [ # ... snip ... path('community/', include('aggregator.urls')), path('contact/', include('contact.urls')), # ... snip ... ]
傳遞額外參數:
# main.py from django.urls import include, path urlpatterns = [ path('blog/', include('inner'), {'blog_id': 3}), ] # inner.py from django.urls import path from mysite import views urlpatterns = [ path('archive/', views.archive), path('about/', views.about), ]
URL的反向解析:
from django.urls import path from . import views urlpatterns = [ #... path('articles/<int:year>/', views.year_archive, name='news-year-archive'), #... ]
<a href="{% url 'news-year-archive' 2012 %}">2012 Archive</a> {# Or with the year in a template context variable: #} <ul> {% for yearvar in year_list %} <li><a href="{% url 'news-year-archive' yearvar %}">{{ yearvar }} Archive</a></li> {% endfor %} </ul>
from django.http import HttpResponseRedirect from django.urls import reverse def redirect_to_year(request): # ... year = 2006 # ... return HttpResponseRedirect(reverse('news-year-archive', args=(year,)))
視圖:它接受Web請求並返回Web響應。此響應能夠是網頁的HTML內容,重定向,404錯誤,XML文檔等
Http404:當您返回錯誤時HttpResponseNotFound
,生成的錯誤頁面的HTML(能夠是HTML文件)
from django.http import Http404 from django.shortcuts import render from polls.models import Poll def detail(request, poll_id): try: p = Poll.objects.get(pk=poll_id) except Poll.DoesNotExist: raise Http404("Poll does not exist") return render(request, 'polls/detail.html', {'poll': p})
上傳文件:
表單:數據存儲在request.FILES ['file']中,若是<form>
表單有enctype="multipart/form-data"
屬性時request.FILES 纔有數據,不然沒有數據
from django import forms class UploadFileForm(forms.Form): title = forms.CharField(max_length=50) file = forms.FileField()
from django.http import HttpResponseRedirect from django.shortcuts import render from .forms import UploadFileForm # Imaginary function to handle an uploaded file. from somewhere import handle_uploaded_file def upload_file(request): if request.method == 'POST': form = UploadFileForm(request.POST, request.FILES) # 注意咱們必須將request.FILES傳入到表單的構造方法中,只有這樣文件數據才能綁定到表單中。 if form.is_valid(): handle_uploaded_file(request.FILES['file']) return HttpResponseRedirect('/success/url/') else: form = UploadFileForm() return render(request, 'upload.html', {'form': form}) # 處理數據的函數 def handle_uploaded_file(f): with open('some/file/name.txt', 'wb+') as destination: for chunk in f.chunks(): # 使用UploadedFile.chunks()而不是read()的英文爲了確保即便大的英文文件又不會將咱們系統內存佔滿。 destination.write(chunk)
模塊處理上傳的文件:
file = models.FileField(upload_to='demo_files/%Y/%m/%d/%H:%M:%S'),/%Y/%m/%d/%H:%M:%S 是自動調用時間函數。
upload_to爲文件路徑(MRDIA_ROOT/upload_to/filename),文件名爲上傳的文件名(不變)
MRDIA_ROOT:setting中的變量,如:os.path.join(BASE_DIR, 'media')
MRDIA_URL:瀏覽器訪問上傳文件的前綴,如:MEDIA_URL = '/media/'
from django.http import HttpResponseRedirect from django.shortcuts import render from .forms import ModelFormWithFileField def upload_file(request): if request.method == 'POST': form = ModelFormWithFileField(request.POST, request.FILES) if form.is_valid(): # file is saved form.save() return HttpResponseRedirect('/success/url/') else: form = ModelFormWithFileField() return render(request, 'upload.html', {'form': form})
上傳多個文件:
from django import forms class FileFieldForm(forms.Form): file_field = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True})) # 視圖函數 from django.views.generic.edit import FormView from .forms import FileFieldForm class FileFieldView(FormView): form_class = FileFieldForm template_name = 'upload.html' # Replace with your template. success_url = '...' # Replace with your URL or reverse(). def post(self, request, *args, **kwargs): form_class = self.get_form_class() form = self.get_form(form_class) files = request.FILES.getlist('file_field') if form.is_valid(): for f in files: ... # 寫入文件函數 return self.form_valid(form) else: return self.form_invalid(form)
快捷函數:django.shortcuts模塊
render:渲染模板並返回response request:請求對象 template_name : 模板名稱 context:添加到模板的字典數據 status:狀態碼 using:加載模塊的模板引擎
from django.shortcuts import render def my_view(request): # View code here... return render(request, 'myapp/index.html', { 'foo': 'bar', }, content_type='application/xhtml+xml')
redirect:重定向
from django.shortcuts import redirect # 經過調用函數 def my_view(request): ... obj = MyModel.objects.get(...) return redirect(obj) # 經過傳遞視圖的名稱和可選的一些位置或關鍵字參數; 使用如下reverse()方法將反向解析URL def my_view(request): ... return redirect('some-view-name', foo='bar') # 經過傳遞硬編碼的URL來重定向到: def my_view(request): ... return redirect('/some/url/')
get_object_or_404(klass,* args,** kwargs):調用get()
給定的模型管理器,但它會引起Http404
而不是模型的 DoesNotExist
異常。
klass:一Model
類,一個Manager
,或QuerySet
從哪一個實例來獲取對象。
**kwargs:查找參數,應採用get()和接受的格式filter()
from django.shortcuts import get_object_or_404 def my_view(request): obj = get_object_or_404(MyModel, pk=1) get_object_or_404(Book, title__startswith='M', pk=1) get_object_or_404(Book.dahl_objects, title='Matilda')
會話:當SessionMiddleware
激活時,每一個HttpRequest
對象將具備一個 session
屬性,這是一個相似字典的對象。
__setitem__: request.session['fav_color'] = 'blue'
get:fav_color = request.session.get('fav_color', 'red')
flush:從會話中刪除當前會話數據並刪除會話cookie。確保用戶沒法從瀏覽器再次訪問之前的會話數據
set_expiry:設置會話的到期時間。您能夠傳遞許多不一樣的值:1.value
是整數,則會話將在多秒不活動後到期。2.若是value
是一個datetime
或timedelta
物體,該會議將在相應的日期/時間到期。3.value
是0
,用戶的會話cookie將在用戶的Web瀏覽器關閉時到期 4.value
是None
,則會話將恢復爲使用全局會話到期策略。
def login(request): m = Member.objects.get(username=request.POST['username']) if m.password == request.POST['password']: request.session['member_id'] = m.id return HttpResponse("You're logged in.") else: return HttpResponse("Your username and password didn't match.")
表單:表單必須指定兩樣東西:1.負責響應用戶輸入數據的URL地址 2.數據請求使用的HTTP方法。
# HTML模板 <form action="/your-name/" method="post"> <label for="your_name">Your name: </label> <input id="your_name" type="text" name="your_name" value="{{ current_name }}"> <input type="submit" value="OK"> </form> # form表單 from django import forms class NameForm(forms.Form): your_name = forms.CharField(label='Your name', max_length=100) # 這樣整個表單在第一次渲染時,會顯示以下: <label for="your_name">Your name: </label> <input id="your_name" type="text" name="your_name" maxlength="100" required> # 視圖函數 from django.http import HttpResponseRedirect from django.shortcuts import render from .forms import NameForm def get_name(request): # if this is a POST request we need to process the form data if request.method == 'POST': form = NameForm(request.POST) if form.is_valid(): # 表單驗證,若是不經過則返回帶有數據的form,讓用戶從新填寫(帶以前的數據) return HttpResponseRedirect('/thanks/') else: form = NameForm() # 空表單 return render(request, 'name.html', {'form': form}) # name.html,全部的表單字段及其屬性都將經過Django模板語言從 {{ form }} 中被解包成HTML標記。 <form action="/your-name/" method="post"> {% csrf_token %} {{ form }} <input type="submit" value="Submit"> </form>
已驗證的表單數據放在 form.cleaned_data
from django.core.mail import send_mail if form.is_valid(): subject = form.cleaned_data['subject'] message = form.cleaned_data['message'] sender = form.cleaned_data['sender'] cc_myself = form.cleaned_data['cc_myself'] recipients = ['info@example.com'] if cc_myself: recipients.append(sender) send_mail(subject, message, sender, recipients) return HttpResponseRedirect('/thanks/')
手動渲染模板:
<p><label for="id_subject">Subject:</label> <input id="id_subject" type="text" name="subject" maxlength="100" required></p> <p><label for="id_message">Message:</label> <textarea name="message" id="id_message" required></textarea></p> <p><label for="id_sender">Sender:</label> <input type="email" name="sender" id="id_sender" required></p> <p><label for="id_cc_myself">Cc myself:</label> <input type="checkbox" name="cc_myself" id="id_cc_myself"></p> # 手動渲染 {{ form.non_field_errors }} <div class="fieldWrapper"> {{ form.subject.errors }} <label for="{{ form.subject.id_for_label }}">Email subject:</label> {{ form.subject }} </div> <div class="fieldWrapper"> {{ form.message.errors }} <label for="{{ form.message.id_for_label }}">Your message:</label> {{ form.message }} </div> <div class="fieldWrapper"> {{ form.sender.errors }} <label for="{{ form.sender.id_for_label }}">Your email address:</label> {{ form.sender }} </div> <div class="fieldWrapper"> {{ form.cc_myself.errors }} <label for="{{ form.cc_myself.id_for_label }}">CC yourself?</label> {{ form.cc_myself }} </div> # 完整的 <label> 元素還可使用 label_tag() 來生成。 <div class="fieldWrapper"> {{ form.subject.errors }} {{ form.subject.label_tag }} {{ form.subject }} </div>
從模型建立表單:django.forms.ModelFrom
字段基本與模型同樣。ForeignKey -> ModelChoiceField,ManyToMaryField -> ModelMultipleChoiceField,ModelChoiceField 和 ModelMultipleChoiceField 的選項爲一個QuerySet
若是模型字段設置了blank = True,那麼表單字段的required屬性被設置爲False,不然爲True。
若是模型字段設置了 choices
,那麼表單字段的 widget
會被設置爲 Select
,其選項來自模型字段的 choices
。
from django.db import models from django.forms import ModelForm TITLE_CHOICES = ( ('MR', 'Mr.'), ('MRS', 'Mrs.'), ('MS', 'Ms.'), ) class Author(models.Model): name = models.CharField(max_length=100) title = models.CharField(max_length=3, choices=TITLE_CHOICES) birth_date = models.DateField(blank=True, null=True) def __str__(self): return self.name class Book(models.Model): name = models.CharField(max_length=100) authors = models.ManyToManyField(Author) class AuthorForm(ModelForm): class Meta: model = Author fields = ['name', 'title', 'birth_date'] class BookForm(ModelForm): class Meta: model = Book fields = ['name', 'authors'] # 等同於如下 from django import forms class AuthorForm(forms.Form): name = forms.CharField(max_length=100) title = forms.CharField( max_length=3, widget=forms.Select(choices=TITLE_CHOICES), ) birth_date = forms.DateField(required=False) class BookForm(forms.Form): name = forms.CharField(max_length=100) authors = forms.ModelMultipleChoiceField(queryset=Author.objects.all())
覆蓋默認字段:要爲字段指定自定義組件,請使用內部 Meta
類的 widgets
屬性。widgets屬性接收一個數據字典。其中每一個元素的鍵必須是模型中的字段名之一,鍵值就是咱們要自定義的內容了
from django.utils.translation import gettext_lazy as _ class AuthorForm(ModelForm): class Meta: model = Author fields = ('name', 'title', 'birth_date') labels = { 'name': _('Writer'), } help_texts = { 'name': _('Some useful help text.'), } error_messages = { 'name': { 'max_length': _("This writer's name is too long."), }, }
模板(存放在 templates 文件夾):用於分割文檔的表示(presentation)和數據(data)的字符串文本。模板定義了佔位符(placeholders)和各類定義文檔應該如何顯示的基本邏輯(即模板標籤,template tag)。一般,模板用來生成 HTML,可是 Ddjango 模板一樣可以生成任何基於文本的格式。{{ }} 裏面是變量,{% %}是模板標籤(須要封閉),能夠是for、if等循環分支語句, | 是過濾器(linux的管道符相似)
配置:
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], # 定義引擎應按搜索順序查找模板源文件的目錄列表。 'APP_DIRS': True, # 引擎是否應該在已安裝的應用程序中查找模板。每一個後端都定義了應該存儲其模板的應用程序內的子目錄的常規名稱。 'OPTIONS': { # ... some options here ... }, }, ] <html> <head><title>Ordering notice</title></head> <body> <p>Dear {{ person_name }},</p> <p>Thanks for placing an order from {{ company }}. It's scheduled to ship on {{ ship_date|date:"F j, Y" }}.</p> <p>Here are the items you've ordered:</p> <ul> {% for item in item_list %} <li>{{ item }}</li> {% endfor %} </ul> {% if ordered_warranty %} <p>Your warranty information will be included in the packaging.</p> {% endif %} <p>Sincerely,<br />{{ company }}</p> </body> </html>
基於類的視圖:從本質上講,基於類的視圖容許您使用不一樣的類實例方法響應不一樣的HTTP請求方法,而不是在單個視圖函數中使用條件分支代碼。
from django.http import HttpResponse def my_view(request): if request.method == 'GET': # <view logic> return HttpResponse('result') ## 等價於 from django.http import HttpResponse from django.views import View class MyView(View): def get(self, request): # <view logic> return HttpResponse('result')
由於Django的URL解析器指望將請求和關聯的參數發送到可調用函數而不是類,因此基於類的視圖具備一個 as_view()
類方法,該方法返回一個函數,當請求到達匹配關聯模式的URL時,能夠調用該函數。該函數建立類的實例,調用setup()
初始化其屬性,而後調用其dispatch()
方法。 dispatch
查看請求以肯定它是否爲GET
, POST
等等,並將請求中繼到匹配方法(若是已定義),或者HttpResponseNotAllowed
若是不是則引起:
# urls.py from django.urls import path from myapp.views import MyView urlpatterns = [ path('about/', MyView.as_view()), ]
配置類屬性:1.在類中直接硬編碼 2.在配置as.view時傳參數
# 第一種 from django.http import HttpResponse from django.views import View class GreetingView(View): greeting = "Good Day" def get(self, request): return HttpResponse(self.greeting) # 第二種 urlpatterns = [ path('about/', GreetingView.as_view(greeting="G'day")), ]