Django学习(2.2.1版本)

项目技术重难点分析:

表单提交与处理、文件的上传、session和cookie、ORM、JQuery、Ajax、json、xml、模板、后台管理、日志调试、缓存、安全。

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

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

模型类的每个属性都相当于一个数据库的字段 

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字段是一个默认字段,如果有主键则被覆盖,默认是不允许为空。

模型的使用:在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, 但是这个改动还没有作用到数据库文件

python manager.py migrate:将该改动作用到数据库文件,比如产生table,修改字段的类型等。

字段:

字段名备注:除了 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'

模板(存放在 templates 文件夹):用于分割文档的表示(presentation)和数据(data)的字符串文本。模板定义了占位符(placeholders)和各种定义文档应该如何显示的基本逻辑(即模板标签,template tag)。通常,模板用来生成 HTML,但是 Ddjango 模板同样能够生成任何基于文本的格式。{{ }} 里面是变量,{% %}是模板标签(需要封闭),可以是for、if等循环分支语句, |  是过滤器(linux的管道符类似)

调用:render(from django.shortcuts import render),第一个参数是resquest,第二个是模板路径,第三个是变量参数(字典)

<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>

猜你喜欢

转载自www.cnblogs.com/xcxy-boke/p/11276872.html