03-django-typeidea-model

参考博客

Django 文档 | Django 文档 | Django
the5fire
表关系中的常用字段:on_delete=models.CASCADE,ForeignKey,db_constraint,related_name
执行python manage.py migrate 没有创建表的解决方案
QuerySet API 参考 | Django 文档 | Django
翻译了Django1.4数据库访问优化部分 | the5fire

创建项目及配置

创建项目结构

cd D:\github\python-django\2.typeidea
django-admin startproject typeidea
python manage.py runserver

拆分settings以适应不同运行环境

mkdir settings && cd settings && copy nul __init__.py
cd ..
move settings.py settings/base.py                      #将base.py抽象成settings的基类
cd settings && copy nul develop.py
  • 修改时区语言
# base.py文件中需要修改的部分,其他部分省略,
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
  • 开发环境使用指定数据库
#将base中的数据库配置粘贴到develop.py,并引入base.py的其他配置
# coding:utf-8
from .base import *   # NOQA
DEBUG = True    #从base.py剪贴过来
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR,'db.sqlite3'),
}
}
  • 修改manage.py和typeidea/wsgi.py。指定settings路径
替换:manage.py和typeidea/wsgi.py中
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'typeidea.settings')

为:
    profile = os.environ.get('TYPEIDEA_PROFILE', 'develop')   #如果环境变量有值就用环境的,没有就用develop
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "typeidea.settings.%s" % profile)

git仓库

配置.gitignore

  • 将不需要上传的git文件配置到.gitignore
*.pyc
*.swp
*.sqlite3

提交命令

git add .
git commit -m "初始提交"
git remote add origin <仓库url>
git push -u origin master

# 安全提交方式
git add -p       交互提交方式
git commit       进入编辑模式

编写Model层代码

  • model分为

创建app/blog

## 创建一个新分支
git checkout -b add-blog-app-model
cd typeidea/typeidea
python manage.py startapp blog

编写blog/model.py

from django.db import models
from django.contrib.auth.models import User

# Create your models here.


class Category(models.Model):
    STATUS_NORMAL = 1
    STATUS_DELETE = 0
    STATUS_ITEMS = (
        (STATUS_NORMAL, '正常'),
        (STATUS_DELETE, '删除'),
    )

    name = models.CharField(max_length=50, verbose_name="名称")
    status = models.PositiveBigIntegerField(default=STATUS_ITEMS,
                                            choices=STATUS_ITEMS, verbose_name="状态")
    is_nav = models.BooleanField(default=False, verbose_name="是否为导航")
    created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")

    class Meta:
        verbose_name = verbose_name_plural = '分类'

class Tag(models.Model):
    STATUS_NORMAL = 1
    STATUS_DELETE = 0
    STATUS_ITEMS = (
        (STATUS_NORMAL, '正常'),
        (STATUS_DELETE, '删除'),
    )

    name = models.CharField(max_length=255,verbose_name="标题")
    status = models.CharField(max_length=1024,blank=True,verbose_name="摘要")
    owner = models.ForeignKey(User,verbose_name="作者", on_delete=models.CASCADE)
    create_time = models.DateTimeField(auto_now_add=True,verbose_name="创建时间")

    class Meta:
        verbose_name = verbose_name_plural = "标签"

class Post(models.Model):
    STATUS_NORMAL = 1
    STATUS_DELETE = 0
    STATUS_DRAFT = 2
    STATUS_ITEMS = (
        (STATUS_NORMAL,'正常'),
        (STATUS_DELETE,'删除'),
        (STATUS_DRAFT,'草稿'),
    )

    title = models.CharField(max_length=255,verbose_name="标题")
    desc = models.CharField(max_length=1024,blank=True,verbose_name="摘要")
    content = models.TextField(verbose_name="正文",help_text="正文必须为MarkDown格式")
    status = models.PositiveIntegerField(default=STATUS_NORMAL,
        choices=STATUS_ITEMS,verbose_name="状态")
    category = models.ForeignKey(Category,verbose_name="分类", on_delete=models.CASCADE)
    tag = models.ManyToManyField(Tag,verbose_name="标签")
    owner = models.ForeignKey(User,verbose_name="作者", on_delete=models.CASCADE)
    created_time = models.DateTimeField(auto_now_add=True,verbose_name="创建时间")

    class Meta:
        verbose_name = verbose_name_plural = "文章"
        ordering = ['-id']  # 根据id进行降序排列

创建app/config,编写model.py

from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Link(models.Model):
    STATUS_NORMAL = 1
    STATUS_DELETE = 0
    STATUS_ITEMS = (
        (STATUS_NORMAL,'正常'),
        (STATUS_DELETE,'删除'),
    )
    title = models.CharField(max_length=50,verbose_name="标题")
    href = models.URLField(verbose_name="链接")      #默认长度为200
    status = models.PositiveIntegerField(default=1,choices=STATUS_ITEMS,
                                         verbose_name="状态")
    weight = models.PositiveIntegerField(default=1,choices=zip(range(1,6),range(1,6)),
                                         verbose_name="权重",
                                         help_text="权重靠前展示顺序靠前")
    owner = models.ForeignKey(User,verbose_name="作者", on_delete=models.CASCADE)
    created_time = models.DateTimeField(auto_now_add=True,verbose_name="创建时间")

    class Meta:
        verbose_name = verbose_name_plural = "友链"

# 侧边栏
class SideBar():
    STATUS_SHOW = 1
    STATUS_HIDE = 0
    STATUS_ITEMS = (
        (STATUS_SHOW,'展示'),
        (STATUS_HIDE,'隐藏'),
    )
    SIDE_TYPE =(
        (1,'HTML'),
        (2,'最新文章'),
        (3,'最热文章'),
        (4,'最近评论'),
    )
    title = models.CharField(max_length=50,verbose_name="标题")
    display_type = models.PositiveIntegerField(default=1,choices=SIDE_TYPE,
                                               verbose_name="展示类型")
    content = models.CharField(max_length=500,blank=True,verbose_name="内容",
                              help_text="如果设置的不是HTML类型,可为空")
    status = models.PositiveIntegerField(default=STATUS_SHOW,choices=STATUS_ITEMS,
                                         verbose_name="状态")
    created_time = models.DateTimeField(auto_now_add=True,verbose_name="创建时间")
    owner = models.ForeignKey(User,verbose_name="作者", on_delete=models.CASCADE)
    created_time = models.DateTimeField(auto_now_add=True,verbose_name="创建时间")

    class Meta:
        verbose_name = verbose_name_plural = "侧边栏"

创建评论app/comment,编写model.py

from django.db import models
from blog.models import Post


# Create your models here.

class Comment(models.Model):
    STATUS_NORMAL = 1
    STATUS_DELETE = 0
    STATUS_ITEMS = (
        (STATUS_NORMAL, '正常'),
        (STATUS_DELETE, '删除'),
    )

    target = models.ForeignKey(Post, verbose_name="评论", on_delete=models.CASCADE)
    content = models.CharField(max_length=2000, verbose_name="内容")
    nickname = models.CharField(max_length=50, verbose_name="昵称")
    website = models.URLField(verbose_name="网站")
    email = models.EmailField(verbose_name="邮箱")
    status = models.PositiveIntegerField(default=STATUS_NORMAL, choices=STATUS_ITEMS, verbose_name="状态")
    created_time = models.DateTimeField(auto_now_add=True, verbose_name="创建时间")

    class Meta:
        verbose_name = verbose_name_plural = "评论"

配置INSTALLED_APPS

  • 编写完model之后,将创建的app配置到settings,使django可以识别到
  • 注意INSTALLED_APPS列表的顺序,static和templates模块寻找app资源时,按从上到下的app顺序查找
  • 开发中可以写一份路径和名称与admin(app)一样的来覆盖自带的,django可以重载


创建数据库

  • 创建迁移文件,就是把model定义的数据库信息迁移到数据库中
python manage.py makemigrations
  • 执行迁移操作
  • 数据库的名称是在settings/develop.py中配置的DATABASES里的default中的name
  • 使用其他类型数据库如mysql需要先建好数据库,通过这2个命令来建表
python manage.py migrate
  • 查看数据库,进入交互模式
python manage.py dbshell
  • 注意数据库库生成的位置


ORM基本概念

常用字段类型

数值型

- AutoField int(11):自增主键,django model默认提供,可以被重写
id = models.AutoField(primary_key=True)

- BooleanField tinyint(1):布尔类型字段,一般用于记录状态

- DecimalField decimal :  对数据精度要求高,支付金融相关
cash = models.DecimalField(max_digits=8,decimal_places=2,default=0,verbose_name="消费金额")
定义长度为8位,精度为2的数字。比如保存666.66这样的数字,max_digits=5,decimal_places=2

- IntegerField int(11):同AutoField一样,就是不自增

- PostivieIntegerField:只包含正整数

- SmallIntegerField smallint:小整数用到

字符型

CharField varchar              基础的varchar类型
URLField                       继承自CharFiled,实现了对url的特殊处理
UUIDField char(22)           在postgreSQL中使用的是uuid类型,其他存放生成的唯一id用char(32)
EmailField                        继承CharFiled,多了对email的特殊处理
FileField                            继承自CharField,多了对文件的处理,admin展示部分会生成一个可上传文件的按钮
TextField longtext            存放大容量文本,博客正文
ImageField                      继承自FileField,处理图片相关数据

日期类型

ForeginKey
OneToOneField
MangToManyField
外键和一对一其实是一种,只是一对一在外键字段上加了unique,多对多会创建一个中间表

参数

null              null,在数据库层面是否允许为空
blank              针对业务层面,该值是否允许为空
choices            配置字段choices,在admin页面上就可以看到对应的可选项展示
db_column           指定Field对应数据库哪个字段,一般是name对应数据库字段
db_index            索引配置,业务上需要经常作为查询条件的字段
default             默认值配置
editable            是否可编辑,默认True,如果不想将字段展示到页面上,可以配置为False
error_messages         用来自定义字段值校验失败时的异常提示,他是字典格式,key的选项可以为null、blank、invalid、invalid_choice、unique、unique_for_date
unique               唯一约束
unique_for_date       针对date(日期)的联合约束,比如我们一天只能有一篇django文章配置字段时配置参数:unique_for_date="created_time"

querySet对象

  • 数据库查询与更新,使用queryset
  • 通过给Model增加一个objects属性来提供数据操作的接口
  • queryset是懒加载的,当用到时才会执行数据库查询语句
  • 支持链式查询

下面2句不会执行数据库查询,只是拿到了queryset对象

  • 当执行print(availabled_posts)时,才会执行数据库查询
posts = Post.objects.all()    #拿到quertset对象,这个对象中包含了我们需要的数据,用到他时会去db中获取数据
availabled_posts= posts.filter(status=1)

# 链式调用:每个函数或者方法的执行结果返回对象本身,这样还能继续调用它的方法

常用的queryset接口

支持链式调用的接口

all 接口:相当于select * from table_name 语句
filter 接口:根据条件过滤,常用等于,不等于,大于,小于。比如能改成like查询
                  Model.objects.filter(content_contains="条件")
exclude 接口:同filter,只是相反的逻辑
reverse 接口:把queryset中的结果颠倒
distinct 接口:用来进行去重查询,产生select distint这样的查询
none 接口:返回空的queryset

不支持链式调用的接口

get 接口:Post.objects.get(id=1)用于查询id为1的文章,如果存在则直接返回对应的Post实例;如果不存在,抛出DoesNotExit异常
  try:
  post =Post.objects.get(id=1)
  except Post.DoesNotExist:
  # 做异常处理

create 接口:用来创建一个Model对象,比如post = Post.objects.create(title="一起学习")

get_or_create接口:根据条件查找,如果没有,就调用create创建

update_or_create接口:同get_or_create,只是用来更新

count接口:用于返回queryset有多少条记录,相当于select count(*) from table_name

lastest 接口:返回最新的一条记录,但需要在Model的Meta中定义:get_latest_by = <用来排序的字段>

earliest接口:同上,返回最早的一条记录

first接口:从当前QuerySet记录中获取第一条

last接口:同上,获取最后一条

exists接口:返回True,或者False,在数据库层面执行select(1)As "a" FROM table_name LIMIT 1的查询,用来判断queryset是否有数据最合适。不要用count

bulk_create接口:同create,用来批量创建记录

in_bulk接口:批量查询,接受2个参数id_list和 filed_name,可以通过Post.objects.in_bulk([1,2,3])查询出id为1、2、3的数据。返回结果是字典类型,字典类型的key为查询条件。

update接口:根据条件批量更新记录。比如Post.objects.filter(owner__name='the5fire').update(title='测试更新')

delete接口:同update,这个接口是用来根据条件批量删除记录。update和delete会触发django的signal

values接口:只需要返回某个字段的值,不需要model实例,可以使用
title_list = Post.objects.filter(category_id=1).values('title')
返回的结果包含dict的queryset,

values_list接口:同values,但是直接返回的是包含元组的queryset
titles_list = Post.objects.filter(category=1).values_list('title')
返回的结果类似
如果只有一个字段,可以通过增加flat=True参数,便于后续处理
title_list = Post.objects.filter(category=1).values_list('title',flat=True)
for title in title_list:
    print(title)

进阶接口

defer接口:把不需要的字段做延迟加载,比如说获取到文章中除正文之外的其他字段。通过posts = Post.object.all().defer('content'),这样拿到的记录就不包含content部分
posts = Post.object.all().defer('content')
for post in posts:
    print(post.content)   #此时会执行数据查询,获取到content
N+1 问题:一条查询请求返回n条数据,一般由外键查询产生的比较多

only接口:同defer相反,比如指向获取title记录,就可以使用only

select_related接口:用来解决外键产生的N+1问题的方案。(只能解决一对多的关联关系)
posts = Post.objects.all()
for post in posts:         #产生数据库查询
    print(post.owner)    #产生额外的数据库查询
代码同上面类似,只是用的是owner
他的解决办法
posts = Post.objects.all().select_related('category')
for post in posts:                  #产生数据库查询,category数据也会一次性查询出来
    print(post.category)
prefetch_related 接口:准备多对多关系的数据,可以通过这个接口来避免N+1查询
比如,post和tag的关系可以通过这种方式来避免:
posts = Posts.object.all().prefetch_related('tag')
for post in posts:    #产生两条查询语句,分别查询post和tag
     print(post)

常用的字段查询

Post.objects.filter(content__contains='查询条件')中的contains就属于字段查询。

  • contians:包含,用来进行like查询
  • icontains:同contains,只是忽略大小写
  • exact:精确匹配
  • iexact:同exact,忽略小写
  • in:指定某个集合,比如Post.objects.filter(id__in=[1,2,3])相当于select * from blog_post where in (1,2,3);
  • gt:大于某个值
  • gte:大于某个值
  • lt:小于某个值
  • lte:小于等于每个值
  • startswith:以某个字符串开头,与contains类似,只会产生LIKE'<关键字>%'这样的sql
  • istartwith:同startswith,忽略大小写
  • endswith:以某个字符串结尾
  • iendswith:忽略大小写
  • range:范围查询,多用于时间范围,如Post.objects.filter(created_time__range=('2018-05-01','2018-06-01'))会产生这样的查询:select 。。。where created_time BETWEEN '2018-05-01' AND '2018-06-01';

关于日期类的查询还有很多,比如date、year和month等,具体等需要时查文档即可。

进阶查询

满足复杂查询,比如select ... where id = 1 or id = 2 这样的查询,就需要进阶查询

  • F表达式
保证数据库的原子性
post = Post.objects.get(id=1)
post.pv = post.pv + 1
pos.save()

from django.db.models import F
post = Post.objects.get(id=1)
post.pv = F('pv') + 1          #保证是原子性操作
pos.save
  • Q表达式
解决or查询
from django.db.models import Q
Post.objects.filter(Q(id=1) | Q(id=2))

解决and查询
Post.objects.filter(Q(id=1) & Q(id=2))
  • Count聚合查询(annotate)
得到某个分类下有多少篇文章
category =  Category.objects.get(id=1)
posts_count = category.post_set.count()

增加属性:annotate
from django.db.models import Count
categories = Category.objects.annotate(posts_count=Count('post'))
print(categories[0].posts_count)
这相当category动态增加了属性posts_count,而这个属性的值来源于Count('post')
  • Sum聚合查询(aggregate)
    同count类似,只是他是用来做合计的
from django.db.models import Sum
Post.objects.aggregate(all_pv=Sum('pv'))
# 输出类似结果:{'all_pv'L487}
还有Avg、Mix、Max等表达式,军用来满足sql查询

你可能感兴趣的:(03-django-typeidea-model)