• Django Model 进阶


    回顾:

    • 定义 models

    • settings.py激活app才能使用models

    • migrations:版本控制,当更改库表结构时可以处理数据

    • 增删改查

    • 常见Field

    模型的价值在于定义数据模型,使用Python的形式操作数据库

    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 IF NOT EXISTS myapp_person(
        id INT(11) NOT NULL AUTO_INCREMENT,
        first_name VARCHAR(30) DEFAULT NULL,
        last_name VARCHAR(30) DEFAULT NULL,
        PRIMARY KEY (id)
    ) ENGINE=InnoDB;

    关系:

    • 一对多 ForeignKey,如厂商和汽车关系

    • 多对多 ManyToManyField,如用户和组关系

    • 一对一 OneToOneField,如饭店和地址关系

    # 多对一关系
    class Manufacturer(models.Model):
        pass
    class Car(models.Model):
        manufacturer = models.ForeignKey('Manufacturer')
    
    # 多对多关系
    class Group(models.Model):
        pass
    class User(models.Model):
        groups = models.ManyToManyField('Group')
    
    # 一对一关系
    class Place(models.Model):
        name = models.CharField(max_length=50)
        address = models.CharField(max_length=80)
    class Restaurant(models.Model):
        place = models.OneToOneField('Place', primary_key=True)
        serves_hot_dogs = models.BooleanField(default=False)
        serves_pizza = models.BooleanField(default=False)

    重写save()方法:

    如保存记录之前,写入日志操作,注意一定要记得调用超类的save()方法,否则不会保存到数据库

    class Blog(models.Model):
        name = models.CharField(max_length=100)
        tagline = models.TextField()
    
        def save(self, *args, **kwargs):
            print('do something')
            super(Blog, self).save(*args, **kwargs)
            print('do something else')

    模型继承:

    • 抽象基类

    只想使用父类来持有一些信息,不想在每个子模型中都敲一遍,这个类永远不会单独使用,使用abstract表明基类,数据库不会建该表

    如auth模块 User继承(AbstractUser)

    class CommonInfo(models.Model):
        name = models.CharField(max_length=100)
        age = models.PositiveIntegerField()
        class Meta:
            abstract = True
            ordering = ['name']
    
    class Student(CommonInfo):
        home_group = models.CharField(max_length=5)
        class Meta(CommonInfo.Meta):
            db_table = 'student_info'

    说明:

    Meta 也可以继承,如果子类不实现Meta则默认会继承

    • 多表继承

    继承一个存在的模型,每个模型都有自己的数据库表。每一个层级下的每个model都是一个真正意义上完整的model,

    每个model都有专属的数据表,都可以查询和创建数据表,

    继承关系在子model和它的每个父类之间都添加一个链接(通过自动创建的OneToOneField来实现)

    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的每个子类都会自动创建一张新数据表,通常情况下,这正是我们想要的操作,

    这是因为子类需要一个空间来存储不包含在基类中的字段数据。但有时,可能只想更改model在Python层的行为实现,

    比如:更改默认的manager,或是添加一个新方法

    代理继承可以做到:为原始模型创建一个代理。可以创建、删除、更新代理model的实例,而且所有数据都可以像使用原始model一样保存

    不同之处在于:可以在代理model中改变默认的排序设置和默认的manager,不会对原始model产生影响

    设置 Meta类中proxy的值为True,就完成了对代理model的声明

    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

    说明:

    MyPerson只是一个代理,数据库中不会建立该表,MyPerson类和它的父类Person操作同一个数据表,

    特别的是,Person的任何实例也可以通过MyPerson访问,反之亦然

    • 多重继承

    多重继承和多表继承没什么大的差别,会自动添加两个OneToOneField,

    需要指出,两个基类的默认自增id要重新命名,否则添加OneToOneField会有问题,也不允许子类重写父类字段

    class Article(models.Model):
        article_id = models.AutoField(primary_key=True)
    
    class Book(models.Model):
        book_id = models.AutoField(primary_key=True)
    
    class BookReview(Book, Article):
        pass

    创建记录时,父类的各个字段需要填写

    b = BookReview.objects.create(headline='T' , body='body' , title=' title')

    Model querys

    回顾:

    • 简单增删改查

    • 返回结果 object QuerySets

    • exclude

    • 反向查询 b.entry_set.all()    可起别名,返回QuerySets

    • 复杂查询 Q

    • 跨关联关系查询,__

    • QuerySet链式过滤

    • F查询

    • 限制返回个数

    查询对象比较

    比较两个model实例,实质在后台,它会比较两个模型主键的值

    所有查询api

    https://docs.djangoproject.com/en/1.11/ref/models/querysets/

    进阶:

    1.  annotate添加注释属性 与聚合函数一起使用返回聚合值,如统计问题的个数,q就有了choice_num这个属性

     

    2.  aggregate 聚合函数返回结果字典

     

    3.  聚合类函数,或注释函数,在django.db.models模块中

    • class Avg(expression, output_field=FloatField, **extra)

    • class Count(expression, distinct=False,  **extra)

    • class Max(expression, output_field=None,  **extra)

    • class Min(expression, output_field=None,  **extra)

    • class Sum(expression, output_field=None,  **extra)

    • class StdDev(expression, sample=False, **extra) # 标准差

    • class Variance(expression, sample=False, **extra) # 方差

    4.  distinct() 去重

    5.  values 返回ValuesQuerySet(类字典),而不是模型实例对象

    6.  vlaues_list与values类似,但返回元组而不是字典

    7.  defer only

    8.  using使用哪个数据库

    9.  select_for_update 返回一个queryset,会锁定相关行直到事务结束

    在支持的数据库上面产生一个 SELECT...FOR UPDATE

    10.  raw 执行原始sql

    11.  get_or_create,有就获取;没就新增,注意返回结果类型和参数defaults含义

    try:
        obj = Person.objects.get(first_name='John', last_name='Lennon')
    except Person.DoesNotExist:
        obj = Person.objects.get(first_name='John', last_name='Lennon', birthday=(1993, 7, 1))
    # Equal
    obj, created = Person.objects.get_or_create(
        first_name = 'John',
        last_name = 'Lennon',
        defaults={'birthday': date(1993, 7, 1)})

    12.  update_or_create

    13.  bulk_create

    14.  in_bulk

    15.  latest、earliest、first、last

     

    事务:

    事务是绑定在view中实现的

    from django.db import transaction
    
    @transaction.non_atomic_requests
    def my_view(request):
        print('do_stuff')
        
    @transaction.atomic
    def viewfunc(request):
        print('do_stuff')
        
    '''
    警告!!!
    虽然这种事务模式的优势在于它的简单性,但在访问量增长到一定的时候会造成很大的性能损耗。
    这是因为为每一个视图开启一个事物会有一些额外的开销。
    另外,这种性能影响还取决于你的应用程序的查询模式以及你的数据库对锁的处理是否高效。
    '''

    自定义管理器:

    • 添加额外管理器方法

    默认objects添加自定义方法,方法需写原生sql。

    添加额外管理器方法是类增加“表级”功能的首选方式(如果要添加行级功能,比如只对某个模型的实例起作用,应使用模型方法,而不是管理器方法)

    额外管理器方法可以返回你想要的任何数据,而不需要返回一个查询及集

    例如,下面这个自定义管理器提供一个with_counts()方法,它返回所有OpinionPoll对象的列表,列表的每项都有一额外num_reponses属性,

    该属性保存一个聚合查询的结果(注:对应的是SQL查询语句中的COUNT(*)生成的项)

    # 添加额外管理器方法
    class PollManager(models.Manager):
        def with_counts(self):
            from django.db import connection
            cursor = connection.cursor()
            cursor.execute("""
            SELECT p.id, p.question, p.poll_date, COUNT(*)
            FROM polls_opinionpoll p, polls_resopnse r
            WHERE p.id = r.poll_id
            GROUP BY p.id, p.question, p.poll_date
            ORDER BY p.poll_date DESC""")
            result_list = []
            for row in cursor.fetchall():
                p = self.model(id=row[0], question=row[1], poll_date=row[2])
                p.num_responses = row[3]
                result_list.append(p)
            return result_list
    
    class OpinionPoll(models.Model):
        question = models.CharField(max_length=200)
        poll_date = models.DateField()
        objects = PollManager()  # 关键点***
    
    class Response(models.Model):
        poll = models.ForeignKey('OpinionPoll')
        person_name = models.CharField(max_length=50)
        response = models.TextField()
    • 添加自定义manager 

    简单的说,就是在模型里添加属性=models.Manager(),或者它的自定义子类,从而实现对库表的操作

    class AuthorManager(models.Manager):
        def get_queryset(self):
            return super(AuthorManager, self).get_queryset().filter(role='A')
    
    class EditorManager(models.Manager):
        def get_queryset(self):
            return super(EditorManager, self).get_queryset().filter(role='E')
    
    class Person(models.Model):
        first_name = models.CharField(max_length=50)
        last_name = models.CharField(max_length=50)
        role = models.CharField(max_length=1, choices=(('A', 'Author'), ('E', 'Editor')))
        people = models.Manager()  # 等同于原始 objects
        authors = AuthorManager()
        editros = EditorManager()
    
    Person.objects.all()
    Person.people.all()
    Person.authors.all()
    Person.editros.all()
  • 相关阅读:
    谈一谈循环的性能提升
    Web前端性能优化的9大问题
    随机获取一种颜色值的三种方法
    ES6还是ES2015?
    history.back(-1)和history.go(-1)的区别
    关于overflow-y:scroll ios设备不流畅的问题
    前端如何压缩图片
    【转】理解JavaScript之闭包
    关于如何给数字排序
    本地缓存localstorage使用
  • 原文地址:https://www.cnblogs.com/jonathan1314/p/7515509.html
Copyright © 2020-2023  润新知