Django学习(2.2.1版本)

项目技术重难点分析:html

模型层:模型是您的数据惟一并且准确的信息来源。它包含您正在储存的数据的重要字段和行为。通常来讲,每个模型都映射一个数据库表。python

每各模型都是一个python的类,这些类继承  django.db.models.Modelmysql

模型类的每一个属性都至关于一个数据库的字段 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

字段名备注:除了 ForeignKeyManyToManyFieldOneToOneField,任何字段类型都接收一个可选的参数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是一个datetimetimedelta物体,该会议将在相应的日期/时间到期。3.value0,用户的会话cookie将在用户的Web浏览器关闭时到期 4.valueNone,则会话将恢复为使用全局会话到期策略。

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")),
]
相关文章
相关标签/搜索