Django model inheritance vs composition, and querying multiple models/tables together - django

I have a Django app that has a number of different models, all with a bunch of common data. Ultimately, I think this question comes down to a decision between inheritance and composition. My current implementation is something like this:
class Thing(models.Model):
foo = models.FooField()
bar = models.BarField()
type = models.CharField()
class A(CommonStuff):
aFoo = models.FooField()
class B(CommonStuff):
bFoo = models.FooField()
With this model, I'm able to query for a Thing using the Thing model's manager. Using the type field on Thing, I can get the child object data by looking at the type field, which contains either 'a' or 'b', and then asking for (i.e.) thing.a.aFoo. This is a feature I like because it's a fairly common operation in my app to get a list of all Thing objects.
I see a couple couple issues here. First, the type field seems unnecessary. Is there way to get at the child data without having to first look up the type? It seems like I could work around this with an instance method that returned the correct object given its type value, or if I really wanted to get rid of the type field, I could iterate over each of the reverse relation fields on Thing, looking for one that doesn't raise a DoesNotExist exception. This feels quite brittle to me though. If I add a new 'subthing' C, I have to update Thing to look for the new type. I could fix this by making Thing and abstract model. That way, A and B get all the fields of Thing and I avoid having to use the type field. Problem, though, is that I lose the ability to perform queries for all Thing objects.
Another model I'm thinking about sort of flips this one on its head by turning the data in Thing into a field on A and B.
class Thing(models.Model):
foo = models.FooField()
bar = models.BarField()
class A(models.Model):
aFoo = models.FooField()
thing = models.OneToOneField(Thing)
class B(models.Model):
bFoo = models.FooField()
thing = models.OneToOneField(Thing)
This version has a few benefits. It gets rid of the type field on Thing, and—at least to me—looks and feels cleaner and less brittle. The problem here, though, is the same as the problem with making Thing abstract in the first version. I lose the ability to query all my 'subthings' together. I can do a query for A objects or a query for B objects, but not both. Can use this version of the model without having to sacrifice the ability to query for all 'subthings'? One possibility is to write a manager that queries both models and returns a QuerySet of all the objects. Does that work?

Related

Detect duplicate inserts when adding many-to-many relation

Let's assume there are two models, A and B:
class A(models.Model):
name = models.CharField(max_length=100)
class B(models.Model):
children = models.ManyToManyField(A)
I'm using b.children.add() method to add instance of A to b:
a = A.objects.get(pk=SOMETHING)
b.children.add(a)
As far as I know, Django by default doesn't allow duplicate many-to-many relationship. So I cannot add same instance of A more than once.
But the problem is here, I fetch instances of A with another query, then loop around them and add them one by one. How can I detect a duplicate relation? Does add() method return something useful?
A look at the source code reveals that Django first checks to see if there are any entries that already exist in the database, and then only adds the new ones. It doesn't return any information to the caller, though.
It's not clear if you actually need to detect duplicates, or if you just want to make sure that they're not being added to the database? If it's the latter then everything's fine. If it's the former, there's no way around hitting the database. If you're really concerned about performance you could always perform the check and update the through table yourself (i.e. re-implement add()).

Why Many to Many relationship with self can't be symmetrical

I'm trying to make a model with many to many relationship to itself and this relationship will also have a specific table that will store some information about the relationship, but I'm running into some problems.
I tried to make a many to many relationship with diferent models like the Django docs say, and it's worked fine in some other point of my application. But now I tried to do something like this:
Let's say that I want a model that represents an object (called Item) that is made by other items and also is used to make some other items. For instance, an object Door is made by wood and lock, but Door will also be used to make a House. I thought in something like this for my models
class Item(models.Model):
name = models.CharField(max_length=100)
items = models.ManyToManyField("self",through='IsMadeBy')
class IsMadeBy(models.Model):
itemResult = models.ForeignKey('Item', related_name='itemResult')
itemPart = models.ForeignKey('Item', related_name='itemPart')
amountUsed = models.PositiveIntegerField()
I'm getting the error message:
Many-to-many fields with intermediate tables must not be symmetrical.
So, adding the argument
symmetrical=False
to my relationship the error stops.
With that being said, I want to know how this really works under the hood. For intance, what the symmetrical means in this context in a database level? I would appreciate if anyone could give examples maybe using SQL statements, since right now my brain can't see the overall situation and really learn this concept of symmetrical relationship in a many to many relationship with self.
Look at the Django docs:
https://docs.djangoproject.com/en/1.10/ref/models/fields/#django.db.models.ManyToManyField.symmetrical
With symmetric relationship if i'm your friend then you my friend too - in Django terms, you have one relation.
With non-symmetric direct and reverse relations can be different, you have related_set. For example, if i'm you manager, you not my manager at the same time, but manager via employee_set can have many employees.

Even with copy.deepcopy(django.model), _meta options are shared

I have several tables with exactly the same structure (columns) but different elements (rows). These tables are spatial grids and each table defines a zooming level, therefore it's not necessary to write a model for each one (at least that's what I thought). My approach was to change the attribute ._meta.db_table to the table I need to do my stuff.
The problem is that if I instantiate an object M, say for level 1 (i.e. M._meta.db_table == "table-level-1"), and then instantiate another object N, say for level 2 (i.e. N._meta.db_table == "table-level-2") that same attribute in object M will change as well. Even if I use a deep copy of it!
Do you have any idea how to fix this?
I'm not sure I understand exactly what you've done, but here's how I would implement multiple models with the same structure.
class MyBase(models.Model):
foo = models.Integer()
# and so on
class Meta:
abstract = True
class MySubOne(MyBase):
pass
class MySubTwo(MyBase):
pass
By setting abstract=True on the base model, Django won't create a table for that model. But it will create a table for each of the models that inherits from it. More info: https://docs.djangoproject.com/en/1.7/topics/db/models/#abstract-base-classes

Django ORM join with another table

I have a table known as messages. In my application, users can send different type of messages. Like forwarding an event, etc. As such, I have columns type and value in that table.
What I want to do is for a particular type, goto a particular table and make sure the value is valid (typically this maps to the id of that table). There could be multiple types, and each one has to be mapped to a different table. Is there a way to logically write this in the built in django ORM? Right now I'm only seeing this feasible if I use straight SQL, which I would rather not if I can get away with it...
Right now I'm doing something like:
Messages.objects.all().filter(Q(user_id=id))...etc
I want to add to the statement above checking the type and for the particular type, check the table associated with it.
It sounds like you have a "polymorphic association". There are a couple ways to do analogous things in Django, but I think the one that most closely matches what you described is the contenttypes module, which uses separate columns for the type and for the value as in your application.
You may just want to define a multi-table inheritance structure. This gets you the same result in that you have multiple types of messages and field inheritance, but you don't have to mess with a type field at all.
class Message(models.Model):
value = models.CharField(max_length=9000)
class Event(Message):
pass
class Tweet(Message):
pass
In view post handler:
...
if request.POST['type'] == 'event':
Event.objects.create(value=request.POST['value'])
elif request.POST['type'] == 'tweet':
Tweet.objects.create(value=request.POST['value'])
...
Then, get your objects:
all_tweets = Tweet.objects.all()
all_messages = Message.objects.all()
for message in all_messages:
try:
event = message.event
except Event.DoesNotExist:
# this message is not an Event
pass
If this sort of structure doesn't work for you, there are two other styles of Model inheritance supported by Django: Abstract base classes and Proxy models. You could also hold a GenericForeignKey as suggested by #AndrewGorcester.

Django: limit queryset to a condition in the latest instance of a related set

I have an hierarchy of models that consists of four levels, all for various good reasons but which to explain would be beyond the scope of this question, I assume.
So here it goes in pseudo python:
class Base(models.Model):
...
class Top(models.Model):
base = FK(Base)
class Middle(models.Model):
top = FK(Top)
created_at = DateTime(...)
flag = BooleanField(...)
class Bottom(models.Model):
middle = FK(Middle)
stored_at = DateTime(...)
title = CharField(...)
Given a title, how do I efficiently find all instances of Base for which that title is met only in the latest (stored_at) Bottom instance of the latest (created_at) Middle instance that has flag set to True?
I couldn't find a way using the ORM, and the way I've seen it, .latest() isn't useful to me on the model that I want to query. The same holds for any convenience methods on the Base model. As I'm no SQL expert, I'd like to make use of the ORM as well as avoid denormalization as much as possible.
Thanks!
So, apparently, without heavily dropping into (some very unwieldy) SQL and not finding any alternative solution, I saw myself forced to resort to denormalized fields on the Base model, just as many as were required for efficiently getting the wanted (filtered) querysets of said model.
These fields were then updated at creation/modificatin time of respective Bottom instances.