models表关系

2020-05-31  本文已影响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="苹果")
上一篇下一篇

猜你喜欢

热点阅读