zoukankan      html  css  js  c++  java
  • Django模型

    Django模型

     

     

     

     

    模型源信息

    模型的增删改查

    增删改查、且并、排序、Q运算等多种查询方式

    模型批量新增

    查看django模型执行SQL语句

    values和values_list

    extra实现别名,条件,排序等

    annotate聚合 计数,求和,平均数

    select_related优化一对一查询

    prefetch_related优化一对多查询

    defer排除不需要的字段

    only仅选择需要的字段

    自定义聚合功能

    原始的查询方式

    不避开模型层

    避开模型层

    模型成员

    objects

    模型序列化成字典

    模型关系

    一对一

    一对多

    多对多

    模型继承

    模型附加

    文件上传

     

     

     

    模型源信息

    class Interactive(models.Model):
        intention = models.TextField(blank=True, null=True))
    
        class Meta:
            managed = False // 不然django管理我们的模型,执行迁移无效
            db_table = 'zndz_manage_interactive'

    模型的增删改查

    class Garde(models.Model):
        className = models.CharField(max_length=32)
        
    class Student(models.Model):
        name = models.CharField(label='姓名', max_length=20, default='', help_text=_('帮助文档。'), )
        age = models.IntegerField(label='年龄')
        garde = models.ForeignKey(Garde,null=True,blank=True,on_delete=models.CASCADE)

    增删改查、且并、排序、Q运算等多种查询方式

    # 进入django shell
    python manage.py shell
    
    // 获取单个对象,使用get记得要是有try except
    Student.objects.get(ID=1)
    
    Student.objcets.all().first()
    
    Student.objcets.all().last()
    
    Student.objcets.all()[:10] # limit 10,limit只能取正,不能取负
    
    Student.objcets.fliter(name='xxx').delete()
    
    Student.objcets.fliter(name='xxx').update(name='xxxx')
    
    Student.objcets.get()
    Student.objcets.get_or_create()
    Student.objcets.update_or_create()
    
    Student.objcets.order_by('-id')[:10] # id最大的20条
    
    Student.objcets.all().distinct() # 去重
    
    // 过滤
    Student.objects.filter(age__gt = 30)  // 获取年龄大于30的
    
    Student.objcets.exclude(age_gt = 30) // 除了年龄大于30的,其余的都获取
    
    // 排序
    Student.objects.all().order_by('-ID') // id倒序 
    
    // 计数 与 判断
    s = Student.objects.filter(age = 30)
    if s.count():
    if s.exists():
        
    // 切片
    Student.objcets.all()[0:5]
     // 会被转换为SQL limit 0,5,注意这和python里的切片不太一样,这里的切片代表的
     // 是limit,且不能为负数,python里的切片可以为负数
     
     // 缓存集
     filter、exclude、all都不会真正的去查询数据库,只有我们在迭代结果集,或
     者获取单个对象属性的时候,它才会去查询数据库。
     
     // 查询条件
    __gt
    __lt
    __grt
    __lte
    __in
    
    __contains // like
    __regex // 正则表达式查询
    __startswith // like
    __endswith // // like
    __exact // 精确等于,即=
     // 以上四种前面如果加i(即ignore)表示忽略大小写,例如iexact
     
    //  django 中的查询条件有时区问题,有两种解决方案
    1.关闭django自定义的时区,直接将setings里的USE_TZ改成False即可
    2.在数据库中创建时区表(麻烦,不推荐)
    
    // 聚合关系
    Student.objects.aggregate(Max("age")) // 最大年龄的学生
    .aggregate(Avg("age"))  .aggregate(Max("age"))  Count|Min|Sum
    
    
    // F对象:可以使用模型的A属性与B属性进行比较
    // 可以实现一个模型的不同属性的算符运算操作
    // 还可以支持算术运算
    Grades.objects.filter(ggirlnum__gt=F('gboynum')) // 找到女生人输大于男生人数的班级
    
    
    // Q对象:过滤器的方法中的关键字参数,条件为And模式,采用逻辑或引入Q对象
    // 可以对条件进行封装,封装之后,可以支持逻辑运算:
    // 与 &
    // 或 |
    // 非 ~
    studentList = Student.objects.filter(Q(pk__lt = 3)|Q(sage__gt=50))  pk_id小于3或年龄大于50岁
    models.User.objects.filter(Q(username='老王') & Q(userpass='admin'))   条件与组合
    models.User.objects.filter(~Q(username='老王'))   条件非表示取反
    可以使用 &(and) |(or)  ~(not) 结合括号进行分组,构造更复杂的Q对象
    filter函数可以传递一个或多个Q对象作为位置参数,如果有多个Q对象,这些参数的逻辑为and

    模型批量新增

    # 模型的批量新增
    product_list_to_insert = []
    product1 = Product()
    product2 = Product()
    product_list_to_insert.append(product1)
    product_list_to_insert.append(product2)
    Product.objects.bulk_create(product_list_to_insert)

    查看django模型执行SQL语句

    # 
    str(Student.objcets.all().query)

    values和values_list

    # 查询部分字段,返回字典形式的结果形式
    Student.objcets.values('name','age') # select name,age
    <QuerySet [{'name': 'aa','age':'23岁'}, {'name': 'bb','age':'24岁'}]>
    
    # 查询部分字段,返回tuple形式
    Student.objcets.values_list('name','age')
    # <QuerySet [('aa','23岁'), ('bb','24岁')]>
    
    # 如果只需要 1 个字段,可以指定 flat=True
    Student.objcets.values_list('name')
    # <QuerySet [('aa',), ('bb',)]>
    
    Student.objcets.values_list('name',flat=True)
    # <QuerySet ['aa', 'bb']>
    
    注意:
    1. values_list 和 values 返回的并不是真正的 列表 或 字典,也是 queryset,
        他们也是 lazy evaluation 的(惰性评估,通俗地说,就是用的时候才真正的去
        数据库查)
    2. 如果查询后没有使用,在数据库更新后再使用,你发现得到在是新内容!!!如果想要旧
        内容保持着,数据库更新后不要变,可以 list 一下
    3. 如果只是遍历这些结果,没有必要 list 它们转成列表(浪费内存,数据量大的时候要
        更谨慎!!!)

    extra实现别名,条件,排序等

    # SELECT name AS old_name FROM teacher
    >>> users = User.objects.all().extra(select={'new':'name'})
    >>> users[1].name
    '东东'
    >>> users[1].new
    '东东'
    >>> str(User.objects.all().extra(select={'new':'name'}).query)
    'SELECT (name) AS `new`, `zndz_user`.`id`, `zndz_user`.`cjrq` FROM `zndz_user`'

    annotate聚合 计数,求和,平均数

    >>> from django.db.models import Count
    >>> users = User.objects.values('name').annotate(count=Count('name'))
    >>> users
    <QuerySet [{'name': '', 'count': 23}, {'name': '东东', 'count': 1}, {'name': '小李', 'count': 2}, {'name': '大内密探008', 'count': 1}, {'name': '毛毛', 'count':
    2}, {'name': '王某人', 'count': 2}, {'name': 'zhangsan', 'count': 1}, {'name': 'dongdong', 'count': 1}, {'name': '吉吉国王', 'count': 1}, {'name': 'dongdong123',
     'count': 3}, {'name': 'coco', 'count': 1}, {'name': '小六子', 'count': 2}, {'name': '旺财', 'count': 1}, {'name': '大内密探00狗', 'count': 1}, {'name': 'dong',
    'count': 1}, {'name': '李某人', 'count': 1}, {'name': '愣头青', 'count': 1}, {'name': '肺炎', 'count': 1}, {'name': 'FDL', 'count': 1}, {'name': '铁憨憨', 'count
    ': 1}, '...(remaining elements truncated)...']>
    
    >>> str(User.objects.values('name').annotate(count=Count('name')).query)
    'SELECT `zndz_user`.`name`, COUNT(`zndz_user`.`name`) AS `count` FROM `zndz_user` GROUP BY `zndz_user`.`name` ORDER BY NULL'
    
    >>> from django.db.models import Avg
    
    >>> from django.db.models import Sum

    select_related优化一对一查询

    # 默认情况下,使用一对一级联查询出来的关联对象,只有在被使用时才会对级联对象进行二次查询
    In [13]: articles = Article.objects.all()[:10]
    
    In [14]: a1 = articles[0]  # 取第一篇
    (0.000) SELECT "blog_article"."id", "blog_article"."title", "blog_article"."author_id", "blog_article"."content", "blog_article"."score" FROM "blog_article" LIMIT 1; args=()
    
    In [15]: a1.title
    Out[15]: u'Django u6559u7a0b_1'
    
    In [16]: a1.author_id
    Out[16]: 5
    
    In [17]: a1.author.name   # 再次查询了数据库,注意!!!
    (0.000) SELECT "blog_author"."id", "blog_author"."name", "blog_author"."qq", "blog_author"."addr", "blog_author"."email" FROM "blog_author" WHERE "blog_author"."id" = 5; args=(5,)
    Out[17]: u'zhen'
    
    
    # 如果想在查的时候把级联对象的信息也查出来,需要使用select_related
    In [18]: articles = Article.objects.all().select_related('author')[:10]
    
    In [19]: a1 = articles[0]  # 取第一篇
    (0.000) SELECT "blog_article"."id", "blog_article"."title", "blog_article"."author_id", "blog_article"."content", "blog_article"."score", "blog_author"."id", "blog_author"."name", "blog_author"."qq", "blog_author"."addr", "blog_author"."email" FROM "blog_article" INNER JOIN "blog_author" ON ("blog_article"."author_id" = "blog_author"."id") LIMIT 1; args=()
    
    In [20]: a1.title
    Out[20]: u'Django u6559u7a0b_1'
    
    In [21]: a1.author.name   # 没有再次查询数据库!!
    Out[21]: u'zhen

    prefetch_related优化一对多查询

    # select_related 是使用 SQL JOIN 一次性取出相关的内容。
    # prefetch_related 用于 一对多,多对多 的情况,这时 select_related 用不了,
    # 因为当前一条有好几条与之相关的内容。
    # prefetch_related是通过再执行一条额外的SQL语句,然后用 Python 把两次SQL查询
    # 的内容关联(joining)到一起
    
    In [24]: articles = Article.objects.all().prefetch_related('tags')[:10]
    
    In [25]: articles
    Out[25]: (0.000) SELECT "blog_article"."id", "blog_article"."title", "blog_article"."author_id", "blog_article"."content", "blog_article"."score" FROM "blog_article" LIMIT 10; args=()
    (0.001) SELECT ("blog_article_tags"."article_id") AS "_prefetch_related_val_article_id", "blog_tag"."id", "blog_tag"."name" FROM "blog_tag" INNER JOIN "blog_article_tags" ON ("blog_tag"."id" = "blog_article_tags"."tag_id") WHERE "blog_article_tags"."article_id" IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); args=(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    <QuerySet [<Article: Django 教程_1>, <Article: Django 教程_2>, <Article: Django 教程_3>, <Article: Django 教程_4>, <Article: Django 教程_5>, <Article: Django 教程_6>, <Article: Django 教程_7>, <Article: Django 教程_8>, <Article: Django 教程_9>, <Article: Django 教程_10>]>
    
    # 不用 prefetch_related 时
    In [9]: articles = Article.objects.all()[:3]
    
    In [10]: for a in articles:
        ...:     print a.title, a.tags.all()
        ...:     
    (0.000) SELECT "blog_article"."id", "blog_article"."title", "blog_article"."author_id", "blog_article"."content", "blog_article"."score" FROM "blog_article" LIMIT 3; args=()
    
    (0.000) SELECT "blog_tag"."id", "blog_tag"."name" FROM "blog_tag" INNER JOIN "blog_article_tags" ON ("blog_tag"."id" = "blog_article_tags"."tag_id") WHERE "blog_article_tags"."article_id" = 1 LIMIT 21; args=(1,)

    defer排除不需要的字段

    # 在复杂的情况下,表中可能有些字段内容非常多,取出来转化成 Python 对象会占用大量的资源。
    # 这时候可以用 defer 来排除这些字段
    
    >>> str(User.objects.all().query)
    'SELECT `zndz_user`.`id`, `zndz_user`.`created_id`, `zndz_user`.`name`, `zndz_user`.`sex`, `zndz_user`.`age`, `zndz_user`.`phys`, `zndz_user`.`is_app_user`, `zndz_user`.`deleted`, `zndz
    _user`.`cjrq` FROM `zndz_user`'
    
    >>> str(User.objects.all().defer('created_id').query)
    'SELECT `zndz_user`.`id`, `zndz_user`.`name`, `zndz_user`.`sex`, `zndz_user`.`age`, `zndz_user`.`phys`, `zndz_user`.`is_app_user`, `zndz_user`.`deleted`, `zndz_user`.`cjrq` FROM `zndz_u
    ser`'

    only仅选择需要的字段

    >>> str(User.objects.all().only('created_id').query)
    'SELECT `zndz_user`.`id`, `zndz_user`.`created_id` FROM `zndz_user`'

    自定义聚合功能

    from django.db.models import Aggregate, CharField
     
     
    class GroupConcat(Aggregate):
        function = 'GROUP_CONCAT'
        template = '%(function)s(%(distinct)s%(expressions)s%(ordering)s%(separator)s)'
     
        def __init__(self, expression, distinct=False, ordering=None, separator=',', **extra):
            super(GroupConcat, self).__init__(
                expression,
                distinct='DISTINCT ' if distinct else '',
                ordering=' ORDER BY %s' % ordering if ordering is not None else '',
                separator=' SEPARATOR "%s"' % separator,
                output_field=CharField(),
                **extra)
    # 我们想把 level, info 一样的 聚到到一起,按时间和发生次数倒序排列,并含有每次日志发生的时间。
    ErrorLogModel.objects.values('level', 'info').annotate(
        count=Count(1), time=GroupConcat('time', ordering='time DESC', separator=' | ')
    ).order_by('-time', '-count')

    原始的查询方式

    学习网址:https://my.oschina.net/liuyuantao/blog/751902

    在模型查询API不够用的情况下,还可以使用原始的SQL语法。Django提供两种方法使用原始的SQL查询,一种是使用Manager.raw()方法,进行原始查询并返回模型实例;另一张是完全避开模型层,直接执行自定义SQL语句。【编写原始的SQL语句时,应该格外小心。 每次使用的时候,都要确保转义了参数中任何用户可以控制的字符,以防受到SQL注入攻击

     

    不避开模型层

    raw()管理器方法用于原始的SQL查询,并返回模型实例。

    Manager.raw(raw_query, params=None, translations=None)

    这个方法执行原始的SQL查询,并返回一个django.db.models.query.RawQuerySet 实例。这个RawQuerySet 实例可以像一般的查询集那样,通过迭代来提供对象实例。

    这里最好通过例子展示一下, 假设存在以下模型::

    class Person(models.Model):
        first_name = models.CharField(...)
        last_name = models.CharField(...)
        birth_date = models.DateField(...)

    你可以像这样执行自定义的SQL语句:

    >>> for p in Person.objects.raw('SELECT * FROM myapp_person'):
    ...     print(p)
    John Smith
    Jane Jones

    当然,这个例子不是特别有趣——和直接使用Person.objects.all()的结果一模一样。但是,raw() 拥有其它更强大的使用方法。

     

     

    模型表的名称

    在上面的例子中,Person表的名称是从哪里得到的?

    通常,Django通过将模型的名称和模型的“应用标签”(你在manage.py startapp中使用的名称)进行关联,用一条下划线连接他们,来组合表的名称。在这里我们假定Person模型存在于一个叫做myapp的应用中,所以表就应该叫做myapp_person。

    更多细节请查看db_table选项的文档,它也可以让你自定义表的名称。

    警告

    传递给 .raw()方法的sql语句并没有任何检查。django默认它会返回一个数据集,但这不是强制性的。 如果查询的结果不是数据集,则会产生一个错误。

    警告

    如果你在mysql上执行查询,注意在类型不一致的时候,mysql的静默类型强制可能导致意想不到的结果发生。 如果你在一个字符串类型的列上查询一个整数类型的值,mysql会在比较前强制把每个值的类型转成整数。例如,如果你的表中包含值'abc'和'def',你查询WHERE mycolumn=0,那么两行都会匹配。要防止这种情况,在查询中使用值之前,要做好正确的类型转换。

    警告

    虽然RawQuerySet可以像普通的QuerySet一样迭代,RawQuerySet并没有实现可以在 QuerySet上使用的所有方法。例如,__bool__()和__len__()在RawQuerySet中没有被定义,所以所有RawQuerySet转化为布尔值的结果都是True。RawQuerySet中没有实现他们的原因是,在没有内部缓存的情况下会导致性能下降,而且增加内部缓存不向后兼容。

     

     

     

    将查询字段映射到模型字段

    raw()方法自动将查询字段映射到模型字段。

    字段的顺序并不重要。 换句话说,下面两种查询的作用相同:

    >>> Person.objects.raw('SELECT id, first_name, last_name, birth_date FROM myapp_person')
    ...
    >>> Person.objects.raw('SELECT last_name, birth_date, first_name, id FROM myapp_person')
    ...

    Django会根据名字进行匹配。 这意味着你可以使用SQL的AS子句来将查询中的字段映射成模型的字段。所以如果在其他的表中有一些Person数据,你可以很容易地把它们映射成Person实例:

    >>> Person.objects.raw('''SELECT first AS first_name,
    ...                              last AS last_name,
    ...                              bd AS birth_date,
    ...                              pk AS id,
    ...                       FROM some_other_table''')

    只要名字能对应上,模型的实例就会被正确创建。 

    又或者,你可以在raw()方法中使用translations 参数。这个参数是一个字典,将查询中的字段名称映射为模型中的字段名称。例如,上面的查询可以写成这样:

    >>> name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
    >>> Person.objects.raw('SELECT * FROM some_other_table', translations=name_map)

    索引访问

    raw()方法支持索引访问,所以如果只需要第一条记录,可以这样写:

    >>> first_person = Person.objects.raw('SELECT * FROM myapp_person')[0]

    然而,索引和切片并不在数据库层面上进行操作。 如果数据库中有很多的Person对象,更加高效的方法是在SQL层面限制查询中结果的数量:

    >>> first_person = Person.objects.raw('SELECT * FROM myapp_person LIMIT 1')[0]

    延迟加载模型字段

    字段也可以像这样被省略:

    >>> people = Person.objects.raw('SELECT id, first_name FROM myapp_person')

    查询返回的Person对象是一个延迟的模型实例。这意味着被省略的字段,在访问时才被加载。例如:

    >>> for p in Person.objects.raw('SELECT id, first_name FROM myapp_person'):
    ...     print(p.first_name, # This will be retrieved by the original query
    ...           p.last_name) # This will be retrieved on demand
    ...
    John Smith
    Jane Jones

    从表面上来看,看起来这个查询获取了first_name和last_name。然而,这个例子实际上执行了3次查询。 只有first_name字段在raw()查询中获取,last_name字符在执行打印命令时才被获取。

    只有一种字段不可以被省略——就是主键。 Django 使用主键来识别模型的实例,所以它在每次原始查询中都必须包含。 如果你忘记包含主键的话,会抛出一个InvalidQuery异常。

     

     

    增加注解

    你也可以在查询中包含模型中没有定义的字段。 例如,我们可以使用PostgreSQL 的age() 函数来获得一群人的列表,带有数据库计算出的年龄:

    >>> people = Person.objects.raw('SELECT *, age(birth_date) AS age FROM myapp_person')
    >>> for p in people:
    ...     print("%s is %s." % (p.first_name, p.age))
    John is 37.
    Jane is 42.
    ...

    raw()方法中传递参数

    如果你需要参数化的查询,可以向raw()方法传递params参数。

    >>> lname = 'Doe'
    >>> Person.objects.raw('SELECT * FROM myapp_person WHERE last_name = %s', [lname])

    params是存放参数的列表或字典。你可以在查询语句中使用%s占位符,或者对于字典使用%(key)s占位符(key替换成字典中相应的key值),无论你的数据库引擎是什么。这些占位符将用params 参数中的值替换。

    注意

    SQLite后端不支持字典,你必须以列表的形式传递参数。

    警告

    不要在原始查询中使用字符串格式化!

    它类似于这种样子:

    >>> query = 'SELECT * FROM myapp_person WHERE last_name = %s' % lname
    >>> Person.objects.raw(query)

    千万不要。

     

    使用params参数可以完全防止SQL注入攻击,它是一种普遍的漏洞,使攻击者可以向你的数据库中注入任何SQL语句。如果你使用字符串格式化,早晚会受到SQL注入攻击。 只要你记住默认使用 params 参数,就可以免于攻击。

     

     

    避开模型层

    有时Manager.raw()方法并不十分好用,你不需要将查询结果映射成模型,或者你需要执行UPDATE、 INSERT以及DELETE查询。在这些情况下,你可以直接访问数据库,完全避开模型层。django.db.connection对象提供了常规数据库连接的方式。为了使用数据库连接,先要调用connection.cursor()方法来获取一个游标对象之后,调用cursor.execute(sql, [params])来执行sql语句,调用cursor.fetchone()或者cursor.fetchall()来返回结果行。

    例如:

    from django.db import connection
    
    def my_custom_sql(self):
        cursor = connection.cursor()
    
        cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [self.baz])
    
        cursor.execute("SELECT foo FROM bar WHERE baz = %s", [self.baz])
        row = cursor.fetchone()
    
        return row

    注意如果你的查询中包含百分号字符,你需要写成两个百分号字符,以便能正确传递参数:

    cursor.execute("SELECT foo FROM bar WHERE baz = '30%'")
    cursor.execute("SELECT foo FROM bar WHERE baz = '30%%' AND id = %s", [self.id])

    如果你使用了不止一个数据库,你可以使用django.db.connections来获取针对特定数据库的连接(以及游标)对象。django.db.connections是一个类似于字典的对象,允许你通过它的别名获取特定的连接。

    from django.db import connections
    cursor = connections['my_db_alias'].cursor()
    # Your code here...

    默认情况下,Python DB API会返回不带字段的结果,这意味着你得到的是一个列表,而不是一个字典。花费一点性能代价之后,你可以返回一个字典形式的结果,像这样:

    def dictfetchall(cursor):
        "Returns all rows from a cursor as a dict"
        desc = cursor.description
        return [
            dict(zip([col[0] for col in desc], row))
            for row in cursor.fetchall()
        ]

    下面是一个体现二者区别的例子:

    >>> cursor.execute("SELECT id, parent_id FROM test LIMIT 2");
    >>> cursor.fetchall()
    ((54360982L, None), (54360880L, None))
    
    >>> cursor.execute("SELECT id, parent_id FROM test LIMIT 2");
    >>> dictfetchall(cursor)
    [{'parent_id': None, 'id': 54360982L}, {'parent_id': None, 'id': 54360880L}]

    连接和游标

    连接和游标主要实现 PEP 249中描述的Python DB API标准——除非它涉及到事务处理。如果你不熟悉Python DB-API,注意cursor.execute()中的SQL语句使用占位符"%s",而不是直接在SQL中添加参数。如果你使用这种方法,底层数据库的库会在必要时自动转义你的参数。还要注意Django 使用"%s"占位符,而不是 SQLite Python 使用的"?"占位符。这是一致性和可用性的缘故。Changed in Django 1.7.

    PEP 249并没有说明游标是否可以作为上下文管理器使用。在Python 2.7之前,由于魔术方法查询(Python ticket #9220)中的一个意想不到的行为,游标可以用作上下文管理器。Django 1.7 明确添加允许使用游标作为上下文管理器的支持。

    将游标作为上下文管理器使用:

    with connection.cursor() as c:
        c.execute(...)

    等价于:

    c = connection.cursor()
    try:
        c.execute(...)
    finally:
        c.close()

    模型成员

    隐性属性:

    开发者没有书写,ORM自动生成的

    如果你把隐性属性手动声明了,系统就不会为你产生隐性属性了。

     

    显性属性:

    开发者手动书写的属性

     

    objects

    objects为ORM的隐性属性,我们默认可以使用对象的objects去查询数据,当然我们也可以显示的声明别名:

    # Create your models here.
    class Student(models.Model):
        name = models.CharField(max_length=32)
    
        mk = models.Manager()
    >>> from demo.models import Student
    >>> Student.objects.all()
    Traceback (most recent call last):
      File "<console>", line 1, in <module>
    AttributeError: type object 'Student' has no attribute 'objects'
    >>> Student.mk.all()
    <QuerySet [<Student: Student object (1)>]>

    我们还可以根据需求重写Manager的某些方法:

    class StudentManager(models.Manager):
    
        def get_queryset(self):
            """
            重写query_set
            :return: 
            """
            return super(StudentManager, self).get_queryset().filter(name__contains='mao')
    
    
    class Student(models.Model):
        name = models.CharField(max_length=32)
        objects = StudentManager()
        
    // 测试
    >>> from demo.models import Student
    >>> Student.objects.all()
    <QuerySet [<Student: Student object (1)>]>
    >>> Student.objects.all().first().name
    'maomao'

    模型序列化成字典

    from django.forms.models import model_to_dict
    from django.core.serializers import serialize
    
    
    # 序列化单个实体为json对象
    user = User()
    user.created_id = created_id
    user.name = request.POST['name']
    user.save()
    return JsonResponse({'code': 200, 'msg': "success", 'data': model_to_dict(user)})
    
    # 序列化多个实体为json对象
    uid = request.POST['id']
    users = User.objects.filter(created_id=uid, delated=0)
    return JsonResponse({'code': 200, 'msg': "success", 'data': serialize('json', users)})
    
    # 以上针对数据库操作发实体都是对象,所以可以使用model_to_dict或serizlize
    # 但如果从数据库查询的不是一个实体对象则不能使用上面两种方式
    users = User.objects.values('id', 'name', 'sex', 'age', 'phys', 'is_app_user').filter(Q(id=uid) & Q(deleted=1))
    # 如上users返回的结果已经是一个字典类型,或Queryset里面封装的是字典类型
    # 使用value或value_list查询的数据直接就是字典类型了

     

    模型关系

    一对一

    使用:

    models.OneToOneField()

    进行关联。

    // 需要其中一张表的主键作为另一张表的外键即可,保留外键的表称为从表
    class Person(models.Model): // 主表
        name = models.CharField(max_length=16)
    
    class Card(models.Model): // 从表
        number = models.CharField(max_length=32)
        person = models.OneToOneField(Person,blank=True,null=True)
        
    // 绑定卡与人的一对一关系,默认情况下,当人被删除的情况下,与人绑定的卡也被删除了,
    // 我们可以使用on_delete参数进行调整
    
    on_delete
        models.CASCADE 默认值
        models.PROTECT 保护模式
       models.SET_NULL 置空模式
       models.SET_DEFAULT 置默认值
       models.SET() 删除的时候重新动态指向一个实体

    应用场景

     用于复杂表的拆分

     扩展新功能

     

     

    实现

     使用外键实现的

     对外键添加了唯一约束

     

    数据删除

     级联表

     主表

     从表

     谁声明关系谁就是从表

     在开发中如何确认主从

     当系统遭遇不可避免毁灭时,只能保留一张表,这个表就是你的主表

     默认特性(CASCADE)

     从表数据删除,主表不受影响

     主表数据删除,从表数据直接删除

     受保护(PROTECT)

     开发中为了防止误操作,我们通常会设置为此模式

     主表如果存在级联数据,删除动作受保护,不能成功

     主表不存在级联数据,可以删除成功

     SET

     SET_NULL

     允许为NULL

     SET_DEFAULT

     存在默认值

     SET()

     指定值

     

    数据获取

     级联数据获取

     主获取从: 隐性属性 默认就是级联模型的名字

     从获取主: 显性属性,就是属性的名字

     

     

    一对多

    使用

    models.ForeginKey()

    数据获取

     级联数据获取

     主获取从: 隐性属性 默认就是级联模型的名字_set

     student_set  也就是Manager子类

     all

     filter

     exculde

     凡是Manager上能使用的函数都能使用

     从获取主: 显性属性,就是属性的名字

     

     

     

    多对多

     开发中很少直接使用多对多属性,而是自己维护多对多的关系

     产生表的时候会产生单独的关系表

     关系表中存储关联表的主键,通过多个外键实现的

     多个外键值不能相等

     级联数据获取

     从获取主

     使用显性属性,属性是Manager子类ManyRelateManager。从.主属性.操作

     主获取从

     也是Manager子类,操作和从操作主完全一样。使用隐性属性:主.从_set.操作

     级联数据

     add

     remove

     clear

     set等

     

     

     ManyRelateManager

     函数中定义的类

     并且父类时一个参数

     动态创建

     

     

    模型继承

     Django中模型支持继承

     默认继承会将通用字段放到父表中,特定字段放在自己的表中,中间用外键连接。

     关系型数据库关系越复杂,效率越低,查询越慢

     父类表中也会存储过多的数据(新增子类,父类也增加)

     使用元信息来解决上面这个问题

     使模型抽象化

     抽象的模型就不会在数据库中产生映射了

     子模型映射出来的表直接包含父模型的字段

     

    class Animal(models.Model):
        name = models.CharField(max_length=20)
    
    
    class Cat(Animal):
        like = models.CharField(max_length=10)
    
    
    class Dog(Animal):
        like = models.CharField(max_length=10)
        
    表结构:
    -- auto-generated definition
    create table demo_animal
    (
      id   integer     not null
        primary key autoincrement,
      name varchar(20) not null
    );
    
    -- auto-generated definition
    create table demo_cat
    (
      animal_ptr_id integer     not null
        primary key
        references demo_animal
          deferrable initially deferred,
      like          varchar(10) not null
    );
    
    -- auto-generated definition
    create table demo_dog
    (
      animal_ptr_id integer     not null
        primary key
        references demo_animal
          deferrable initially deferred,
      like          varchar(10) not null
    );
    
    // 可以看到在子类中都将父类Animal作为了他们的外键参考,且外键作为主键唯一,
    // 相当于一对一关联!这也就导致了新增一个子类,就新增一个父类!其实,我们只想
    // 将公共的字段抽象提取出来,并不想在数据库中也将父表建立出来,这时我们应该在
    // 父类的元信息上做说明

    抽象模型类记得要加抽象说明

    class Animal(models.Model):
        name = models.CharField(max_length=20)
        class Meta:
            abstract = True

     

    模型附加

    文件上传

    <form action="{% url 'demo:register' %}" method="post" enctype="multipart/form-data">
        {% csrf_token %}
        用户名:
        <input type="text" name="username">
        头像:
        <input type="file" name="img">
        <input type="submit">
    </form>
    
    // 万金油写法
    def register(request):
        if request.method == 'POST':
            # username = request.POST.get('username')
            
            
            
            img = request.FILES.get('img')
            BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
            with open(os.path.join(BASE_DIR, 'static/upload/imgs/', img.name), 'wb') as f:
                for chunk in img.chunks():
                    f.write(chunk)
                    f.flush()
    
    
    
            return HttpResponse('注册成功!')
        else:
            return redirect(to=reverse('demo:register'))
    // 使用Django model模型提供的上传
    class User(models.Model):
        name = models.CharField(max_length=16)
        img = models.ImageField(upload_to='imgs/%Y')
        // 这里面的upload_to属性是以settings文件中MEDIA_ROOT属性指定的值
        // 为相对路径的,所有我们需要在settings文件中配置一下MEDIA_ROOT
        // 在upload_to中支持日期写法%Y年,%M月,日时分秒等
        // 需要安装Pillow包,用于Django验证图片
    
    // settings配置文件最后加入上传相对路径  
    MEDIA_ROOT = os.path.join(BASE_DIR, 'static/upload')
    
    def register(request):
        if request.method == 'POST':
            username = request.POST.get('username')
            img = request.FILES.get('img')
    
            user = User()
            user.name = username
            user.img = img
            user.save()
            return HttpResponse('注册成功!')
        else:
            return redirect(to=reverse('demo:register'))

    //如果上传同名文件,Django还会帮我们在文件名后加入一些混淆码来区别,这给使用用户
    // 头像等带来了很大方便

     

    前进时,请别遗忘了身后的脚印。
  • 相关阅读:
    iphone dev 入门实例5:Get the User Location & Address in iPhone App
    iphone dev 入门实例4:CoreData入门
    iphone dev 入门实例3:Delete a Row from UITableView
    iphone dev 入门实例2:Pass Data Between View Controllers using segue
    iphone dev 入门实例1:Use Storyboards to Build Table View
    Learning Core Data 1
    Apple dev travel
    数字信封工作原理
    Linux编程概念
    ubuntu16.04 安装 libnfc
  • 原文地址:https://www.cnblogs.com/liudaihuablogs/p/13462860.html
Copyright © 2011-2022 走看看