英文:
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_ptr
的OneToOneField
,用于模型Frame
和Tire
等。因此,如果我们自己进行继承,它会像这样:
class Item(TimestampedModel):
# 一些数据...
pass
class Frame(models.Model):
item_ptr = models.OneToOneField(Item, <b>related_name='tire'</b>)
# 额外的字段
这意味着从Item
到Frame
有一个关联关系:确实.frame
是当Item
是Frame
时获取特定于Frame
的数据。
这也意味着您可以创建Item
的额外子类,这些子类将具有到Frame
的字段,但不能将其命名为frame
,因为由于相关关系,该名称已经被“占用”。例如,您可以决定将它们命名为component_frame
。这就是tires
未列出的原因:tires
和tire
不冲突。因此,“简单”的修复方法是:
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
的表,但Bike
、Tire
等模型将使用您在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…
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)
# …</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:
<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)</code></pre>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论