博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Django基础 - 06Model模型的关联关系及对象继承

Posted on 2023-04-16 20:17  Kingdomer  阅读(31)  评论(0编辑  收藏  举报

 

一、 一对一关系: 实名认证表

一对一关系: models.OneToOneField

主表的数据是相对重要的(UserEntity), 从表 需要 主动声明关系(RealProfile)

对象获取: 从表获取主表数据, 直接使用字段, 对象.字段名.属性名; 主表获取从表数据: 隐性的, 对象.模型名.属性名

1.1 声明一对一关系

class RealProfile(models.Model):
    # 声明一对一的关联关系 OneToOneField
    user = models.OneToOneField(UserEntity, verbose_name='账号', on_delete=models.CASCADE)
    real_name = models.CharField(max_length=20, verbose_name='真实姓名')
    id_number = models.CharField(max_length=18, verbose_name='证件号')
    id_type = models.IntegerField(choices=((0, '身份证'), (1, '护照'), (2, '驾驶证')), verbose_name='证件类型')
    image1 = models.ImageField(verbose_name='正面照片', upload_to='user/real')
    image2 = models.ImageField(verbose_name='反面照片', upload_to='user/real')

    class Meta:
        db_table = 't_real_profile'
        verbose_name = '实名认证'
        verbose_name_plural = verbose_name

  

class RealProfileAdmin(admin.ModelAdmin):
    list_display = ('user', 'real_name', 'id_number', 'id_type')

admin.site.register(RealProfile, RealProfileAdmin)

 

 

1.2 从表获取主表的数据、 主表获取从表的数据 

>>> from mainapp.models import UserEntity, RealProfile
>>> u1 = RealProfile.objects.filter(real_name='李哪吒').first()
>>> u1.user.phone     # 从表访问主表数据
'15577778888'
>>> login_u1 = UserEntity.objects.get(pk=1) >>> login_u1.realprofile.id_number # 主表访问从表数据, 模型类的全小写 '3900110011'

1.3 级联关系

on_delete=models.CASCADE 级联删除/ models.SET_NULL级联关系设置为null

class CartEntity(models.Model):
    user = models.OneToOneField(UserEntity, verbose_name='账号', on_delete=models.CASCADE)
    no = models.CharField(primary_key=True, max_length=10, verbose_name='购物车编号')

    class Meta:
        db_table = 't_cart'
        verbose_name = verbose_name_plural = '购物车表'

    def __str__(self):
        return self.user.name + "(" + self.no + ")"

  

class CartEntityAdmin(admin.ModelAdmin):
    list_display = ('user', 'no')

admin.site.register(CartEntity, CartEntityAdmin)

  

 

二、 一对多关系: 声明购物车与水果的关系表

一对多关系: 使用ForeignKey关系实现; models.ForeignKey

对象获取: 从表获取主表数据, 字段属性

主表获取从表数据: 对象.模型名_set; 结果是数据集合, Manager类型, 支持filter,exclude,all,last,first等方法

2.1 定义一对多关系

class FruitCartEntity(models.Model):
    cart = models.ForeignKey(CartEntity, on_delete=models.CASCADE, verbose_name='购物车')
    fruit = models.ForeignKey(FruitEntity, on_delete=models.CASCADE, verbose_name='水果')
    cnt = models.IntegerField(verbose_name='数量', default=1)
@property def price(self): return round(self.cnt * self.fruit.price, 2) # 显式单个商品的合计
class Meta: db_table = 't_fruit_cart' verbose_name = verbose_name_plural = '购物车详情表' def __str__(self): return self.fruit.name + ':' + self.cart.no  

  

class FruitCartEntityAdmin(admin.ModelAdmin):
    list_display = ('cart', 'fruit', 'cnt', 'price')

admin.site.register(FruitCartEntity, FruitCartEntityAdmin)

2.2 展示计算属性的verbose_name名称

2.2.1 属性方法def price(self) 在后台显示时没有verbose_name, 如何解决

list_display = ('cart', 'fruit', 'cnt', 'price', 'fruit.price') # 使用fruit.price报错
ERRORS:
<class 'mainapp.admin.FruitCartEntityAdmin'>: (admin.E108) The value of 'list_display[4]' ref
ers to 'fruit.price', which is not a callable, an attribute of 'FruitCartEntityAdmin', or an
attribute or method on 'mainapp.FruitCartEntity'.

 

2.2.2 解决办法: 显示字段可以引用关系实体对象的属性

class FruitCartEntity(models.Model):
    @property
    def price(self):
        return round(self.cnt * self.fruit.price, 2)

    @property
    def price_v(self):
        return self.fruit.price

class FruitCartEntityAdmin(admin.ModelAdmin):
    list_display = ('cart', 'fruit', 'cnt', 'price_v_title', 'price_title')

    def price_title(self, obj):
        return obj.price

    def price_v_title(self, obj):
        return obj.price_v

    price_title.short_description = '小计'
    price_v_title.short_description = '单价'

 

2.3  主表 读取 从表数据

>>> from mainapp.models import UserEntity
>>> login_u = UserEntity.objects.get(pk=1)
# UserEntity 与 CartEntity 是一对一关系, CartEntity 与 FruitCartEntity是 一对多关系
>>> login_u.cartentity.fruitcartentity_set <django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager.<locals> .RelatedManager object at 0x0000006F5513C4F0>
>>> login_u.cartentity.fruitcartentity_set.all() <QuerySet [<FruitCartEntity: 苹果:CT001>, <FruitCartEntity: 神仙果:CT001>]>

  

2.4 一对多关系案例: 一个种类对应多种水果 

class FruitEntity(models.Model):

    # category = models.ForeignKey(CategoryEntity, on_delete=models.CASCADE)
    category = models.ForeignKey(CategoryEntity, related_name='fruits',
                                 to_field='id', on_delete=models.CASCADE)

 

>>> from mainapp.models import CategoryEntity
>>> f1.category.name
'南方水果'
>>> c1 = f1.category
>>> c1.fruitentity_set
<django.db.models.fields.related_descriptors.create_reverse_many_to_one_manager.
    <locals>.RelatedManager object at 0x000000C8C826E370>

>>> cat1 = CategoryEntity.objects.get(pk=1)
>>> cat1.name
'热带水果'
>>> cat1.fruits.values()
<QuerySet [{'id': 1, 'name': '火龙果', 'price': 11.50, 'source': '泰国', 'category_id': 1}, 
           {'id': 3, 'name': '神仙果', 'price': 395.59, 'source': '非洲', 'category_id': 1}, 
           {'id': 4, 'name': '荔枝', 'price': 10.78, 'source': '福州', 'category_id': 1}]>

>>> cat1.fruits.values_list() <QuerySet [(1, '火龙果', 11.50, '泰国', 1), (3, '神仙果', 395.59, '非洲', 1),(4, '荔枝', 10.78, '福州', 1)]>

 

三、 多对多关系

使用第三方表建立多对多关系

3.1 定义多对多关系: 用户收藏商品 

class FruitEntity(models.Model):

    # 默认情况下, 反向引用的名称是当前类的名称(小写)_set,可以通过 related_name 来指定;
    # db_table='t_collect' 使用第三张表建立 fruit 和user 的多对多关系
    users = models.ManyToManyField(UserEntity, db_table='t_collect',
                                   related_name='fruits', verbose_name='收藏用户列表',
                                   blank=True, null=True)

准备数据

>>> from mainapp.models import UserEntity, FruitEntity
>>> FruitEntity.objects.get(pk=1)
<FruitEntity: 火龙果>
>>> FruitEntity.objects.get(pk=2)
<FruitEntity: 苹果>
>>> u1 = UserEntity.objects.get(pk=1)
>>> u1.name
'哪吒'
>>> u2 = UserEntity.objects.get(pk=2)
>>> u2.name
'宋江'
>>> f1 = FruitEntity.objects.get(pk=2)
>>> f1.name
'苹果'

3.2 多对多关系的CRUD操作

### 给用户u1/u2 添加水果
>>> u1.fruits.add(FruitEntity.objects.get(pk=1))
>>> u1.fruits.add(FruitEntity.objects.get(pk=2))
>>> u2.fruits.add(FruitEntity.objects.get(pk=2))

### 查找水果f1 有哪些用户收藏, 查询用户的信息
>>> f1.users.all()
<QuerySet [<UserEntity: 哪吒>, <UserEntity: 宋江>]>
>>> f1.users.values_list() <QuerySet [(1, '哪吒', 3, '15577778888'), (2, '宋江', 47, '15566660000')]>
>>> f1.users.values() <QuerySet [{'id': 1, 'name': '哪吒', 'age': 3, 'phone': '15577778888'}, {'id': 2, 'name': '宋江', 'age': 47, 'phone': '15566660000'}]> ### 查询用户收藏了 哪些水果 >>> u1.fruits.values() <QuerySet [{'id': 1, 'name': '火龙果', 'price': 11.508304793776945, 'source': '泰国', 'category_id': 1}, {'id': 2, 'name': '苹果', 'price': 2.013953338910966, 'source': '烟台', 'category_id': 2}]>
### 用户取消收藏的水果 >>> u1.fruits.remove(FruitEntity.objects.get(pk=1)) >>> u1.fruits.values() <QuerySet [{'id': 2, 'name': '苹果', 'price': 2.013953338910966, 'source': '烟台', 'category_id': 2}]>   

  

3.3 多对多关系案例: 水果与标签

3.3.1 定义标签类

class TagEntity(models.Model):
    name = models.CharField(max_length=50, unique=True, verbose_name='标签名')
    order_num = models.IntegerField(default=1, verbose_name='序号')

    class Meta:
        db_table = 't_tag'
        verbose_name = verbose_name_plural = '标签管理'
        ordering = ['-order_num']

    def __str__(self):
        return self.name

  

3.3.2 定义多对多关系

class FruitEntity(models.Model):

    tags = models.ManyToManyField(TagEntity, db_table='t_fruit_tags',
                                  related_name='tag_fruits', verbose_name='所有标签')

  

3.3.3 多对多关系的CRUD操作

### pk=1的水果 添加 两个标签
>>> FruitEntity.objects.get(pk=1).tags.add(TagEntity.objects.get(pk=1)) >>> FruitEntity.objects.get(pk=1).tags.add(TagEntity.objects.get(pk=2))
### pk=1的标签对应的水果 >>> TagEntity.objects.get(pk=1).tag_fruits.values() <QuerySet [{'id': 1, 'name': '火龙果', 'price': 11.50, 'source': '泰国', 'category_id': 1}]>
### pk=1的水果 包含的标签 >>> FruitEntity.objects.get(pk=1).tags.values() <QuerySet [{'id': 1, 'name': '会员半价', 'order_num': 1}, {'id': 2, 'name': '七天包退', 'order_num': 1}]> ### pk=2的水果 包含的标签 >>> FruitEntity.objects.get(pk=2).tags.values() <QuerySet []>

 

四、模型类的对象继承

将父模型抽象; 父模型的字段会复制在子模型中

4.1 基础类

class BaseModel(models.Model):
    create_time = models.DateTimeField(verbose_name='创建时间', auto_now_add=True, null=True)
    update_time = models.DateTimeField(verbose_name='更新时间', auto_now=True,     null=True)

    class Meta:
        abstract = True  # 抽象的模型类, 不会创建表

4.2 定义OrderModel, 继承BaseModel

class OrderModel(BaseModel):
    id = models.CharField(max_length=20, primary_key=True, verbose_name='订单号')
    title = models.CharField(max_length=100, verbose_name='订单名称')
    price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='金额')
pay_type = models.IntegerField(choices=((0, '余额'),(1,'银行卡'),(2, '微信支付'), (3, '支付宝')), verbose_name='支付方式', default=0) order_status = models.IntegerField(choices=((0, '待支付'), (1, '已支付'), (2, '待收货'), (3, '已收货'), (4, '完成'), (5, '取消')), verbose_name='订单状态', default=0) receiver = models.CharField(max_length=20, verbose_name='收货人姓名') receiver_phone = models.CharField(max_length=11, verbose_name='收货人手机号') receiver_addr = models.CharField(max_length=100, verbose_name='收货地址') class Meta: db_table = 't_order' verbose_name = '订单表' verbose_name_plural = verbose_name def __str__(self): return self.title

4.3 编写orderapp/admin.py

class OrderAdmin(admin.ModelAdmin):
    list_display = ('id', 'title', 'price', 'pay_type', 'order_status', 'create_time')
    fields = ('id', 'title', 'price')
    list_filter = ('order_status', )

admin.site.register(OrderModel, OrderAdmin)

 

 

五、 模型类的自关联