Hello everyone. I’m playing around with multi-table inheritance to understand if this approach fits my requirements and I have a questions regarding accessing child instances from parent queryset.
Let’s say I have some code:
class Component(models.Model):
maker = models.CharField()
model = models.CharField()
class Cpu(Component):
frequency = models.CharField()
class Motherboard(Component):
socket = models.CharFIeld()
class ComponentSetup(models.Model):
component = models.ForeignKey(Component)
setup = models.ForeignKey(Setup)
qty = models.PositiveIntegerField()
class Setup(models.Model):
components = models.ManyToMany(Component, through="ComponentSetup")
class Computer(models.Model):
type = models.CharField()
setup = models.ForeignKey(Setup)
So the Component
model was made to avoid declaring lot of fields in Setup
model (such as Motherboard
, Cpu
, Memory
etc.). So Setup
requires queryset of Component
instances.
The question:
Once Setup
created how can I render in template data for every Component
child?
Okay, there might be loop for all components with weird checking every instance or doing the same loop within view and passing context. What is a common approach for such a goal or this models design is not right from beginning ?
Keep in mind that templates don’t care whether variables exist or not. If the issue is simply rendering this data, it’s a lot easier just to try rendering the related class.
For example:
(This is just to give you an idea and is not intended to be complete code.)
{% for component in setup.components.all %}
{{ component.maker }}
{{ component.model }}
{{ component.cpu.frequency }}
{{ component.motherboard.socket }}
{% endfor %}
If you don’t want the blank space for a component that doesn’t exist, you could wrap these up in an {% if
tag.
(Note: You’ll want to ensure your queries in the view are properly using the select_related
and prefetch_related
functions to minimize the number of queries being executed for this.)
1 Like
So, to extract child (let’s say Cpu
instance) fields: maker
and model
I have to filter()
the parent class for exact child I need and, if I understood right, there is only way to check if Cpu
in Component
queryset is just using if
or try
to catch the match?
The Cpu
model doesn’t have fields maker
and model
. Those fields are part of the Component
model. Those fields exist in every instance of Component
.
There is no Cpu
in a queryset for Component
, there are only instances of Component
. Some of those Component
may be related to an instance of Cpu
, but the presence or absence of a related Cpu
has no effect on the instance of Component
.
So yes - you can either use an if
or a try / catch
block to determine whether any specific instance of Component
has a related Cpu
instance.
@KenWhitesell thank you for helping me to understand some common things.
And the last question, if I may, to cover some gap in my understanding of topic above:
If I go from oposite letting user / customer to create his own Setup
where some of the components predefined (as computer won’t start without cpu or motherboard) but some components are optional. How user can pick particular Component
’s child using a form if there is relationg with Component
instances but not Gpu
, for example?
I’m sorry, I’m not following what you’re asking here.
As a general principle, always remember that multi-table inheritance is implemented as two tables joined by a OneToOneField. The most significant difference is that the child model can reference the fields of the parent without specifying the fk field name in the expression. If you keep this in mind, I think it would help you understand what needs to be done.
Also keep in mind that structurally, you could have both a Cpu
and a Motherboard
related to the same Component
. I’m not saying that this makes logical sense in your situation - only that it’s physically possible for this to occur. Again, this is one of those cases where understanding how this is being implemented in the database may add clarity to your thought processes for what you may need to do in your code.
1 Like
Thanks a lot for clearing my doubts, @KenWhitesell!