美文网首页
models表关系

models表关系

作者: 小吉头 | 来源:发表于2020-05-31 09:54 被阅读0次

    一、一对多

    以作者和书为例,一个作者可以写多篇文章,一篇文章只能属于一个作者,这就是一对多的关系。

    class Author(models.Model):
        name = models.CharField(max_length=100)
    
    
    class Article(models.Model):
        title = models.CharField(max_length=100,null=False)
        price = models.FloatField(null=False,default=0)
        author = models.ForeignKey('Author',on_delete=models.CASCADE,null=True)
    

    新增

    #正向创建,通过外键字段关联。已知作者,创建文章,通过文章正向关联作者
    def one_to_many_create_view(request):
        author = Author.objects.get(pk=1)
        article = Article(title="测试文章1",price=100,author=author)
        article.save()
        return HttpResponse("success")
    
    #反向创建,通过外键对象反过来关联。已知作者,创建文章,通过作者反向关联文章
    def one_to_many_create_view(request):
        author = Author.objects.get(pk=1)
        article = Article(title="测试文章2", price=100)
        author.article_set.add(article)
        return HttpResponse("success")
    

    反向创建报错如下:
    ValueError: <Article: 测试文章2> instance isn't saved. Use bulk=False or save the object first.
    按照提示需要先把article对象保存,然后再通过author对象反向添加或者设置bulk=False
    (1)添加save():

    #修改反向创建,先save article对象
    def one_to_many_create_view(request):
        author = Author.objects.get(pk=1)
        article = Article(title="测试文章2", price=100) #创建的时候先不通过正向方式设置作者字段
        article.save() #保存成功的前提是author字段可以为null或者models里面设置了default值,否则会报错
        author.article_set.add(article)
        return HttpResponse("success")
    

    (2)设置bulk=False

    #避免了使用save()的前提条件,会自动将article对象先保存
    def one_to_many_create_view(request):
        author = Author.objects.get(pk=1)
        article = Article(title="测试文章2", price=100)
        author.article_set.add(article,bulk=False)
        return HttpResponse("success")
    

    author.article_set.add(article1,article2,article3...,bulk=False)源码如下:
    add(self, *objs, **kwargs) (RelatedManager in django.db.models.fiedls.related_descriptors)

     def add(self, *objs, **kwargs):
                self._remove_prefetched_objects()
                bulk = kwargs.pop('bulk', True)
                objs = list(objs)
                db = router.db_for_write(self.model, instance=self.instance)
    
                def check_and_update_obj(obj):
                    if not isinstance(obj, self.model):
                        raise TypeError("'%s' instance expected, got %r" % (
                            self.model._meta.object_name, obj,
                        ))
                    setattr(obj, self.field.name, self.instance)
    
                if bulk:
                    pks = []
                    for obj in objs:
                        check_and_update_obj(obj)
                        if obj._state.adding or obj._state.db != db:
                            raise ValueError(
                                "%r instance isn't saved. Use bulk=False or save "
                                "the object first." % obj
                            )
                        pks.append(obj.pk)
                    self.model._base_manager.using(db).filter(pk__in=pks).update(**{
                        self.field.name: self.instance,
                    })
                else:
                    with transaction.atomic(using=db, savepoint=False):
                        for obj in objs: 
                            check_and_update_obj(obj)
                            obj.save()
    

    删除

    def one_to_many_delete(request):
        #正向删除
        article = Article.objects.get(id=1)
        article.author.delete() #Article中ForeignKey设置的on_delete=models.CASCADE,所以删除作者的同时会将其对应的文章都删除on_delete还可以设置null或者默认值,可自行查阅
        
        #反向删除
        author = Author.objects.get(id=1)
        author.article_set.all().delete()
        return HttpResponse("success")
    

    反向删除要取.all(),得到queryset对象才有delete()方法,否则只取author.article_set得到的是RelatedManager对象,没有delete()方法。
    和多对多删除不同,反向获取的article_set是RelatedManager对象,没有remove()方法和clear()方法,所以没法通过remove()删除某个对象,只能通过delete()删除全部或者把article_set.all()换成article_set.filter(xxx)进行过滤到指定对象后删除

    查询

    #正向获取,包含外键字段。已知文章,查询对应的作者
    def one_to_many_view(request):
        article= Article.objects.get(id=1)
        author = article.author
        print(author)
        return HttpResponse("success")
    
    #反向获取,通过外键对象反过来获取。已知作者,查询对应的所有文章
    def one_to_many_view(request):
        author = Author.objects.get(pk=1)
        all_articles = author.article_set.all() #article_set 是django自动生成,格式是:外键字段所在的模型类名小写_set
        for article in all_articles:
            print(article)
        return HttpResponse("success")
    

    反向获取时,如果想修改默认的外键字段所在的模型类名小写_set,可以通过设置models中外键字段的releated_name属性
    author = models.ForeignKey('Author',on_delete=models.CASCADE,null=True,related_name="articles")
    author.article_set就失效了,替换为自定义的author.articles

    二、一对一

    用户表和用户扩展信息表,一个扩展信息只能对应一个用户,一个用户只能对应一个扩展信息,这就是一对一关系

    class User(models.Model):
        name = models.CharField(max_length=100)
    
    
    class UserExtra(models.Model):
        hobby = models.CharField(max_length=100)
        user = models.OneToOneField("User",on_delete=models.CASCADE)
    

    新增

    #正向创建:已知用户,创建扩展信息,通过扩展信息关联用户
    def one_to_one_fourth_view(request):
        user_obj = User.objects.get(pk=1)
        user_extra_obj = UserExtra(hobby="读书",user=user_obj)
        user_extra_obj.save()
        return HttpResponse("success")
    

    修改代码如下,两个扩展信息指向同一个用户:

    def one_to_one_third_view(request):
        user_obj = User.objects.get(pk=1)
        user_extra_obj = UserExtra(hobby="读书", user=user_obj)
        user_extra_obj.save()
        user_extra_obj = UserExtra(hobby="旅游",user=user_obj)
        user_extra_obj.save()
        return HttpResponse("success")
    
    #抛异常
    >>django.db.utils.IntegrityError: (1062, "Duplicate entry '1' for key 'user_id'")
    

    右击mysql中UserExtra表,查看对象信息,查看DDL语句如下:

    CREATE TABLE `user_userextra` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `hobby` varchar(100) NOT NULL,
      `user_id` int(11) NOT NULL,
      PRIMARY KEY (`id`),
      UNIQUE KEY `user_id` (`user_id`)
    ) ENGINE=MyISAM AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
    

    一对一是严格规定的,不允许出现一对多的情况,UserExtra表中将user这个一对一字段自动设置了unique防止重复

    反向创建:已知用户,创建扩展信息,通过用户关联扩展信息

    无法通过user_obj.userextra.add()方式添加扩展对象,因为user_obj.userextra是一个UserExtra类对象,没有add()方法

    删除

    def one_to_one_delete(request):
        #正向删除
        user_obj = User.objects.get(pk=1)
        user_obj.userextra.delete() # user_obj.userextra得到的是UserExtra类对象
        
        #反向删除
        user_extra_obj = UserExtra.objects.get(id=1)
        user_extra_obj.user.remove()# user_extra_obj.user得到的是User类对象
        return HttpResponse("success")
    

    查询

    def one_to_one_view(request):
        #正向查询,已知扩展信息,查询对应的用户
        user_extra_obj = UserExtra.objects.get(id=1)
        user_obj = user_extra_obj.user
        print(user_obj)
    
        #反向查询:已知用户,查询对应的扩展信息
        user_obj = User.objects.get(pk=1)
        user_extra_obj = user_obj.userextra #userextra是django自动生成,格式是:外键字段所在的模型类名小写
        print(user_extra_obj)
        return HttpResponse("success")
    

    反向获取时,如果想修改默认的外键字段所在的模型类名小写,可以通过设置models中外键字段的releated_name属性
    user = models.OneToOneField("User",on_delete=models.CASCADE,related_name="extra")
    user_obj.userextra就失效了,替换为自定义的user_obj.extra

    三、对多对

    商品和订单是多对多关系

    #商品表
    class Goods(models.Model):
        name = models.CharField(max_length=100) #商品名称
        price = models.FloatField() #商品价格
        order = models.ManyToManyField("Order")
    
    
    #订单表
    class Order(models.Model):
        order_num = models.CharField(max_length=100) #订单号
        create_time = models.DateTimeField(auto_now_add=True)#创建时间
    

    新增

    def many_to_many_create(request):
        # 正向添加单个
        good = Goods.objects.get(pk=1)
        order = Order.objects.get(pk=1)
        good.order.add(order)#也可以用add(id)比如add(1)
        good.order.set([order])#跟add(order)效果一样
    
        #正向添加多个
        good = Goods.objects.get(pk=1)
        orders = Order.objects.all()
        good.order.add(*orders)#也可以用add([id1,id2,id3])比如add([1,2,3]),不会删除之前的关联,只会新添加关联,如果已存在则覆盖
        good.order.set(orders) #注意这里是一个可迭代queryset,效果跟add(*orders)一样,不会删除之前的关联,只会新添加关联,如果已存在则覆盖
    
        #正向添加多个
        good = Goods.objects.get(pk=1)
        orders = Order.objects.all()
        good.order = orders #这里如果只添加单个对象会抛'Order' object is not iterable异常
        good.save()
    
        #反向添加单个
        good = Goods.objects.get(pk=1)
        order = Order.objects.get(pk=1)
        order.goods_set.add(good)
        order.goods_set.set([good])#跟add(good)效果一样
    
        #反向添加多个
        goods = Goods.objects.all()
        order = Order.objects.get(pk=1)
        order.goods_set.add(*goods)
        order.goods_set.set(goods)#跟add(*goods)效果一样
    
        return HttpResponse("success")
    

    删除第三张表关系

    def many_to_many_delete(request):
        #正向删除单个
        good = Goods.objects.get(pk=1)
        order = Order.objects.get(pk=1)
        good.order.remove(order)
    
        # 正向删除多个
        good = Goods.objects.get(pk=1)
        orders = Order.objects.all()
        good.order.remove(*orders)
    
        # 正向删除多个
        good = Goods.objects.get(pk=1)
        good.order.clear()
    
        #反向删除单个
        good = Goods.objects.get(pk=1)
        order = Order.objects.get(pk=1)
        order.goods_set.remove(good)
    
        # 反向删除多个
        goods = Goods.objects.all()
        order = Order.objects.get(pk=1)
        order.goods_set.remove(*goods)  #虽然goods是全部商品,但是反向删除只会删除订单对象对应的所有商品
    
        #反向删除多个
        order = Order.objects.get(pk=1)
        order.goods_set.clear()
    
        return HttpResponse("success")
    

    删除第三张表关系和真实数据

        #正向删除单个
        good = Goods.objects.get(pk=1)
        order = Order.objects.get(pk=1)
        good.order.all().delete()
        # 反向删除多个
        goods = Goods.objects.all()
        order = Order.objects.get(pk=1)
        order.goods_set.all().delete() 
    

    需要取到.all(),得到queryset对象才有delete()方法,否则只取good.order或者order.goods_set得到的是ManyRelatedManager对象,没有delete()方法。

    修改

    先删除,再新增

    查询

    def many_to_many_view(request):
        #正向查询,通过商品查所有订单
        good = Goods.objects.get(pk=1)
        orders = good.order.all()
        print(orders)
        
        #正向查询,通过订单某个字段查所有商品
        goods = Goods.objects.filter(order__order_num="2000")
        print(goods)
        
        # 正向查询,通过商品表查询,查询订单对应的所有商品
        goods = Goods.objects.filter(order=1)
        print(goods)
        
        #反向查询,通过订单查所有商品
        order = Order.objects.get(pk=1)
        goods = order.goods_set.all()
        print(goods)
        
        #反向查询,通过商品某个字段查所有订单
        orders = Order.objects.filter(goods__name="苹果")
        print(orders)
        
        #反向查询,通过订单表查询,查询商品主键对应的所有订单
        orders = Order.objects.filter(goods=1)
        print(orders)
    
        return HttpResponse("success")
    

    跟一对多类似,反向获取时,如果想修改默认的外键字段所在的模型类名小写_set,可以通过设置models中ManyToManyField类的releated_name属性自定义名称

    通过字段查询,不管正向反向都用对方model名称的小写__字段名

    #正向查询,通过订单某个字段查所有商品
    goods = Goods.objects.filter(order__order_num="2000")
    #反向查询,通过商品某个字段查所有订单
    orders = Order.objects.filter(goods__name="苹果")
    

    相关文章

      网友评论

          本文标题:models表关系

          本文链接:https://www.haomeiwen.com/subject/ycnrohtx.html