具有其他模型作为外键,但都继承自相同模型。

huangapple go评论107阅读模式
英文:

Model with other models as foreign keys but all inherited from the same model

问题

I am building an e-commerce backend for bicycles using Django and DRM. I have bikes and bike parts. I want to make them all items and want to relate each bike to the parts it was built from.

Item model:

class Item(TimeStampedModel):
    ID_LENGHT = 6
    
    item_id = models.CharField(
        primary_key=True,
        max_length=ID_LENGHT,
        unique=True,
        editable=False,
        blank=False
        )  
    name = models.CharField(max_length=255)
    active = models.BooleanField(default=False)
    quantity = models.PositiveIntegerField()
    unit_price = models.DecimalField(max_digits=10, decimal_places=2)

One of the parts models:

class Tire(Item):    
    type = models.CharField(max_length=50, choices=TIRE_TYPES)

Bike model:

class Bike(Item):
    frame = models.ForeignKey(Frame, on_delete=models.CASCADE)
    tires = models.ForeignKey(Tire, on_delete=models.CASCADE)
    seat = models.ForeignKey(Seat, on_delete=models.CASCADE)
    wheel = models.ForeignKey(Wheel, on_delete=models.CASCADE)

Errors:

SystemCheckError: System check identified some issues:

ERRORS:
bikeshop.Bike.frame: (models.E006) The field 'frame' clashes with the field 'frame' from model 'bikeshop.item'.
bikeshop.Bike.seat: (models.E006) The field 'seat' clashes with the field 'seat' from model 'bikeshop.item'.
bikeshop.Bike.wheel: (models.E006) The field 'wheel' clashes with the field 'wheel' from model 'bikeshop.item'.

I want to add attachments and other related items. Item model was just abstract, and because of that, I have an issue with the shopping cart. After reading this article, I decided to make the base model concrete but ran into this new issue.

英文:

I am building an e-commerce backend for bicycles using Django and DRM. I have bikes and bike parts. I want to make them all items and want to relate each bike to the parts it was built from.

Item model:

class Item(TimeStampedModel):
    ID_LENGHT = 6
    
    item_id = models.CharField(
        primary_key=True,
        max_length=ID_LENGHT,
        unique=True,
        editable=False,
        blank=False
        )  
    name = models.CharField(max_length=255)
    active = models.BooleanField(default=False)
    quantity = models.PositiveIntegerField()
    unit_price = models.DecimalField(max_digits=10, decimal_places=2)

One of the parts models:

class Tire(Item):    
    type = models.CharField(max_length=50, choices=TIRE_TYPES)

Bike model:

class Bike(Item):
    frame = models.ForeignKey(Frame, on_delete=models.CASCADE)
    tires = models.ForeignKey(Tire, on_delete=models.CASCADE)
    seat = models.ForeignKey(Seat, on_delete=models.CASCADE)
    wheel = models.ForeignKey(Wheel, on_delete=models.CASCADE)

Errors:

SystemCheckError: System check identified some issues:

ERRORS:
bikeshop.Bike.frame: (models.E006) The field 'frame' clashes with the field 'frame' from model 'bikeshop.item'.
bikeshop.Bike.seat: (models.E006) The field 'seat' clashes with the field 'seat' from model 'bikeshop.item'.
bikeshop.Bike.wheel: (models.E006) The field 'wheel' clashes with the field 'wheel' from model 'bikeshop.item'.

I want to add attachments and other related items. Item model was just abstract and because of that I have an issue with shopping cart. After reading this article I decided to make base model concrete but run into this new issue. I tried to add related_name= to the foreign keys but it hasn't solved the issue.

答案1

得分: 0

以下是翻译好的部分:

原因是因为您使用了具体继承。的确,Item是一个具体(非抽象)模型,您在许多额外的模型中继承了Item

这意味着Django将构建一个名为Item的模型,并在其后面创建一个数据库表,并创建名为item_ptrOneToOneField,用于模型FrameTire等。因此,如果我们自己进行继承,它会像这样:

class Item(TimestampedModel):
    # 一些数据...
    pass

class Frame(models.Model):
    item_ptr = models.OneToOneField(Item, <b>related_name='tire'</b>)
    # 额外的字段

这意味着从ItemFrame有一个关联关系:确实.frame是当ItemFrame时获取特定于Frame的数据。

这也意味着您可以创建Item的额外子类,这些子类将具有到Frame的字段,但不能将其命名为frame,因为由于相关关系,该名称已经被“占用”。例如,您可以决定将它们命名为component_frame。这就是tires未列出的原因:tirestire不冲突。因此,“简单”的修复方法是:

class Bike(Item):
    <b>component_frame</b> = models.ForeignKey(Frame, on_delete=models.CASCADE)
    <b>component_tires</b> = models.ForeignKey(Tire, on_delete=models.CASCADE)
    # ...

但即使您设法这样做,仍然很繁琐。关系数据库没有对子类的“本机”支持。类的概念根本不存在。虽然有几种建模子类的方法,但几乎总是会导致复杂的查询。

如果您不必引用Item本身,而总是引用Item的“子类”,另一种方法是将Item设置为抽象,这样它将不会构建这些OneToOneField。在这种情况下,没有名为Item的表,但BikeTire等模型将使用您在Item中定义的字段:

class Item(TimeStampedModel):
    ID_LENGTH = 6
    item_id = models.CharField(
        primary_key=True,
        max_length=ID_LENGTH,
        editable=False,
    )
    name = models.CharField(max_length=255)
    active = models.BooleanField(default=False)
    quantity = models.PositiveIntegerField()
    unit_price = models.DecimalField(max_digits=10, decimal_places=2)

    class Meta:
        <b>abstract = True</b>

class Bike(Item):
    frame = models.ForeignKey(Frame, on_delete=models.CASCADE)
    tires = models.ForeignKey(Tire, on_delete=models.CASCADE)
    seat = models.ForeignKey(Seat, on_delete=models.CASCADE)
    wheel = models.ForeignKey(Wheel, on_delete=models.CASCADE)
英文:

The reason this happens is because you work with concrete inheritance. Indeed, Item is a concrete (non-abstract) model, and you inherit Item in a lot of extra models.

This means that Django will construct a model Item with a database table behind it, and make models like Frame and Tire with a OneToOneField to the Item named item_ptr. So if we would do the inheritance ourselves, it would look like:

<pre><code>class Item(TimestampedModel):
# some data&hellip;
pass

class Frame(models.Model):
item_ptr = models.OneToOneFIeld(Item, <b>related_name='tire'</b>)
# extra fields</code></pre>

So that means there is a relation from Item to Frame: indeed .frame is when an Item is a Frame to obtain the data specific to the Frame.

This thus means that you can make extra subclasses of Item that will have a field to Frame, but you can not name these frame, since that name is already "taken" because of the related relation. You can for example decide to name these component_frame. This is the reason that tires is not listed: tires and tire do not conflict. So an "easy" fix is:

<pre><code>class Bike(Item):
<b>component_</b>frame = models.ForeignKey(Frame, on_delete=models.CASCADE)
<b>component_</b>tires = models.ForeignKey(Tire, on_delete=models.CASCADE)
# &hellip;</code></pre>

But even if you manage to do that, it is still cumbersome. Relational databases have no "native" support for subclasses. The concept of classes does not exist at all. While there are several ways to model subclassing, it nearly always results in complicated queries anyway.

If you don't have to reference to an Item itself, but always to "subclasses" of an Item, an alternative way is to make Item abstract, in which case it will not construct these OneToOneField. In that case there is no table named Item, but the Bike, Tire, etc. models will all use the fields you defined in Item:

<pre><code>class Item(TimeStampedModel):
ID_LENGHT = 6
item_id = models.CharField(
primary_key=True,
max_length=ID_LENGHT,
editable=False,
)
name = models.CharField(max_length=255)
active = models.BooleanField(default=False)
quantity = models.PositiveIntegerField()
unit_price = models.DecimalField(max_digits=10, decimal_places=2)

class Meta:
    &lt;b&gt;abstract = True&lt;/b&gt;

class Bike(Item):
frame = models.ForeignKey(Frame, on_delete=models.CASCADE)
tires = models.ForeignKey(Tire, on_delete=models.CASCADE)
seat = models.ForeignKey(Seat, on_delete=models.CASCADE)
wheel = models.ForeignKey(Wheel, on_delete=models.CASCADE)</code></pre>

huangapple
  • 本文由 发表于 2023年7月4日 20:37:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/76612723.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定