django学习笔记数据库(二)

1.外键:增加数据操作

#models.py
#函数,可以提高代码的利用率
def set_default():
	return Category.object.get(pk=2)
class Category(models.Model):
    name=models.CharField(max_length=100)
class Article(models.Model):
    title=models.CharField(max_length=100)
    content=models.TextField()
    category=models.ForeignKey("Category",on_delete=models.CASCADE,bull=True)
    
#设置默认值    
category=models.ForeignKey("Category",on_delete=models.SET_DEFAULT,default=Category.objects.get(pk=2))
category=models.ForeignKey("Category",on_delete=models.SET(Category.objects.get(pk=2)))
category=models.ForeignKey("Category",on_delete=models.SET(set_default))
#使用的是别的app的外键,    
forntuser=models.ForeignKey("app_name.frontname",on_delete=models.CASCADE)
#on_delete=donothing,完全依靠数据库级别的约束
#views.py
def indexxxx(request):
    category=Category(name="最新文章")
    category.save()
    article=Article(title="标题",content="这是内容")
    article.category=category
    article.save()
    return HttpResponse("success 添加文章和标签")

外键引用自己,可以用于盖楼式评论

class comment(models.Model):
    content=models.TextField()
    origin_comment=models.ForeignKey("self",on_delete=models.CASCADE)
    
#或者写成    origin_comment=models.ForeignKey("comment",on_delete=models.CASCADE)
#或者写成    origin_comment=models.ForeignKey("article.comment",on_delete=models.CASCADE)

2.外键:删除数据操作

#views.py,级联操作,category删除时,文章也删除
def delete_view(request):
    category=Category.objects.get(pk=8)
    category.delete()
    return HttpResponse("delete success")

表关系笔记:

一对多:

  1. 应用场景:比如文章和作者之间的关系。一个文章只能由一个作者编写,但是一个作者可以写多篇文章。文章和作者之间的关系就是典型的多对一的关系。
  2. 实现方式:一对多或者多对一,都是通过ForeignKey来实现的。还是以文章和作者的案例进行讲解。
class User(models.Model):
    username = models.CharField(max_length=20)
    password = models.CharField(max_length=100)

class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    author = models.ForeignKey("User",on_delete=models.CASCADE)

那么以后在给Article对象指定author,就可以使用以下代码来完成:

article = Article(title='abc',content='123')
author = User(username='zhiliao',password='111111')
# 要先保存到数据库中
author.save()
article.author = author
article.save()

并且以后如果想要获取某个用户下所有的文章,可以通过article_set来实现。示例代码如下:

user = User.objects.first()
# 获取第一个用户写的所有文章
articles = user.article_set.all()
for article in articles:
    print(article)

获取一个标签下的所有文章

#获取一个标签的所有文章
category=Category.objects.first()
articles=category.article_set.all()
for article in articles:
    print(article)

并且如果想要将文章添加到某个分类中。可以使用一下的方式:

category = Category.objects.first()
article = Article(title='bbb',content='vvv')
article.author = FrontUser.objects.first()
article.save()
category.article_set.add(article,bulk=False)
category.save()
  • 使用bulk=False,那么Django会自动的保存article,而不需要在添加到category之前先保存article。
  • 或者是另外一种解决方式是,在添加到category.article_set中之前,先将article保存到数据库中。但是如果article.category不能为空,那么就产生一种死循环了,article没有category不能保存,而将article添加到cateogry.artile_set中,又需要article之前是已经存储到数据库中的。
  • 如果是上面的那种需求,建议使用bulk=False的解决方案。

一对一:

  1. 在Django中一对一是通过models.OnetToOneField来实现的。这个OneToOneField其实本质上就是一个外键,只不过这个外键有一个唯一约束(unique key),来实现一对一。
  2. 以后如果想要反向引用,那么是通过引用的模型的名字转换为小写的形式进行访问。比如以下模型:
    class FrontUser(models.Model):
        username = models.CharField(max_length=200)
    
    class UserExtension(models.Model):
        school = models.CharField(max_length=100)
        user = models.OneToOneField("FrontUser",on_delete=models.CASCADE)
    
    # 通过userextension来访问UserExtension对象
    user = FrontUser.objects.first()
    print(user.userextension)
    
def one_to_one_view(request):
	user=FrontUser.objects.first()
	extension=UserExtension(schoole='zhiliao')
	extension.user=user
	extension.save()
	return HttpResponse("success")
`UserExtension`的对象,可以通过`user`来访问到对应的user对象。并且`FrontUser`对象可以使用`userextension`来访问对应的`UserExtension`对象。
如果不想使用Django默认的引用属性名字。那么可以在`OneToOneField`中添加一个`related_name`参数。示例代码如下:
```python
class FrontUser(models.Model):
    username = models.CharField(max_length=200)

class UserExtension(models.Model):
    school = models.CharField(max_length=100)
    user = models.OneToOneField("FrontUser",on_delete=models.CASCADE,related_name='extension')

# 通过extension来访问到UserExtension对象
user = FrontUser.objects.first()
print(user.extension)
```
那么以后就`FrontUser`的对象就可以通过`extension`属性来访问到对应的`UserExtension`对象。

多对多:

  1. 应用场景:比如文章和标签的关系。一篇文章可以有多个标签,一个标签可以被多个文章所引用。因此标签和文章的关系是典型的多对多的关系。

  2. 实现方式:Django为这种多对多的实现提供了专门的Field。叫做ManyToManyField。还是拿文章和标签为例进行讲解。示例代码如下:

class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    tags = models.ManyToManyField("Tag",related_name="articles")

class Tag(models.Model):
    name = models.CharField(max_length=50)

在数据库层面,实际上Django是为这种多对多的关系建立了一个中间表。这个中间表分别定义了两个外键,引用到articletag两张表的主键。

查询条件笔记:

  1. exact:在底层会被翻译成=

  2. iexact:在底层会被翻译成LIKE

    • LIKE和=:大部分情况下都是等价的,只有少数情况下是不等价的。
    • exict和iexact:他们的区别其实就是LIKE和=的区别,因为exact会被翻译成=,而iexact会被翻译成LIKE。
    • 因为field__exact=xxx其实等价于filed=xxx,因此我们直接使用filed=xxx就可以了,并且因为大部分情况exactiexact又是等价的,因此我们以后直接使用field=xxx就可以了。
  3. QuerySet.query:query可以用来查看这个ORM查询语句最终被翻译成的SQL语句。但是query只能被用在QuerySet对象上,不能用在普通的ORM模型上。因此如果你的查询语句是通过get来获取数据的,那么就不能使用query,因为get返回的是满足条件的ORM模型,而不是QuerySet。如果你是通过filter等其他返回QuerySet的方法查询的,那么就可以使用query

  4. contains:使用大小写敏感的判断,某个字符串是否在指定的字段中。这个判断条件会使用大小敏感,因此在被翻译成SQL语句的时候,会使用like binary,而like binary就是使用大小写敏感的。

  5. icontains:使用大小写不敏感的判断,某个字符串是否被包含在指定的字段中。这个查询语句在被翻译成SQL的时候,使用的是like,而likeMySQL层面就是不区分大小写的。

  6. contains和icontains:在被翻译成SQL的时候使用的是%hello%,就是只要整个字符串中出现了hello都能过够被找到,而iexact没有百分号,那么意味着只有完全相等的时候才会被匹配到。

  7. in:可以直接指定某个字段的是否在某个集合中。示例代码如下:

    articles = Article.objects.filter(id__in=[1,2,3])
    

    也可以通过其他的表的字段来判断是否在某个集合中。示例代码如下:

    categories = Category.objects.filter(article__id__in=[1,2,3])
    

    如果要判断相关联的表的字段,那么也是通过__来连接。并且在做关联查询的时候,不需要写models_set,直接使用模型的名字的小写化就可以了。比如通过分类去查找相应的文章,那么通过article__id__in就可以了,而不是写成article_set__id__in的形式。当然如果你不想使用默认的形式,可以在外键定义的时候传递related_query_name来指定反向查询的名字。示例代码如下:

    class Category(models.Model):
        name = models.CharField(max_length=100)
    
        class Meta:
            db_table = 'category'
    
    
    class Article(models.Model):
        title = models.CharField(max_length=200)
        content = models.TextField()
        cateogry = models.ForeignKey("Category",on_delete=models.CASCADE,null=True,related_query_name='articles')
    
        class Meta:
            db_table = 'article'
    

    因为在cateogryForeignKey中指定了related_query_namearticles,因此你不能再使用article来进行反向查询了。这时候就需要通过articles__id__in来进行反向查询。

    反向查询是将模型名字小写化。比如article__in。可以通过related_query_name来指定自己的方式,而不使用默认的方式。
    反向引用是将模型名字小写化,然后再加上_set,比如article_set,可以通过related_name来指定自己的方式,而不是用默认的方式。

    并且,如果在做反向查询的时候,如果查询的字段就是模型的主键,那么可以省略掉这个字段,直接写成article__in就可以了,不需要这个id了。

    in不仅仅可以指定列表/元组,还可以为QuerySet。比如要查询“文章标题中包含有hello的所有分类”,那么可以通过以下代码来实现:

    articles = Article.objects.filter(title__icontains='hello')
    categories = Category.objects.filter(articles__in=articles)
    for cateogry in categories:
        print(cateogry)
    
  8. gt、gte、lt、lte:代表的是大于、大于等于、小于、小于等于的条件。示例代码如下:

    articles = Article.objects.filter(id__lte=3)
    
  9. startswith、istartswith、endswith、iendswith:表示以某个值开始,不区分大小写的以某个值开始、以某个值结束、不区分大小写的以某个值结束。示例代码如下:

    articles = Article.objects.filter(title__endswith="hello")
    
  10. 关于时间的查询条件:

    • range:可以指定一个时间段。并且时间应该标记为aware时间,不然django会报警告。示例代码如下:

      start_time = make_aware(datetime(year=2018,month=4,day=4,hour=17,minute=0,second=0))
      end_time = make_aware(datetime(year=2018,month=4,day=4,hour=18,minute=0,second=0))
      articles = Article.objects.filter(create_time__range=(start_time,end_time))
      print(articles.query)
      print(articles)
      
    • date:用年月日来进行过滤。如果想要使用这个过滤条件,那么前提必须要在MySQL中添加好那些时区文件。如何添加呢?参考教案。示例代码如下:

      articles = Article.objects.filter(create_time__date=datetime(year=2018,month=4,day=4))
      
    • year/month/day:表示根据年/月/日进行查找。示例代码如下:

      articles = Article.objects.filter(create_time__year__gte=2018)
      
    • week_day:根据星期来进行查找。1表示星期天,7表示星期六,2-6代表的是星期一到星期五。比如要查找星期三的所有文章,那么可以通过以下代码来实现:

      articles = Article.objects.filter(create_time__week_day=4)
      
    • time:根据分时秒来进行查找。如果要具体到秒,一般比较难匹配到,可以使用区间的方式来进行查找。区间使用range条件。比如想要获取17时/10分/27-28秒之间的文章,那么可以通过以下代码来实现:

      start_time = time(hour=17,minute=10,second=27)
      end_time = time(hour=17,minute=10,second=28)
      articles = Article.objects.filter(create_time__time__range=(start_time,end_time))
      

版权声明:本文为qq_42092076原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。