Implementing polymorphic associations on an inventory database - django

I'm trying to design an Inventory table on a database (Django + PostgreSQL) and I came to a doubt as to which is the most "correct" way to implement multiple polymorphic relationships with other tables.
Basically, each entry needs to have an input field which can refer to any of a set of tables: for example, a product could have been purchased (and therefore this field should be a Foreign Key for the PurchaseOrder table), or it could have been produced/manufactured (i.e. ProductionOrder). Similarly, it will eventually have an output which could, for instance, refer to a SalesOrder (if the product is sold) or another ProductionOrder (if it is used to manufacture another thing).
Of course a single Foreign Key field cannot refer to entries in different tables, so what is the best way to achieve this? I read answers to similar questions, but the suggestions didn't seem to be well-suited for a case like this. Creating intermediate tables doesn't seem appropiate since sales and production orders don't belong to the same "superset", at least to me.
My current thought is to have as many fields as possible options (i.e. idProductionOrder, idSalesOrder) for both inputs and outputs, and add a -NAND- constraint to avoid both fields to contain data simultaneously. I see this could be a practical solution, but perhaps not a very neat one. Is there a better approach?

I think what you are looking for is the GenericForeignKey field. This can point to different models. Here an example based on your input:
class YourModel(models.Model):
limit = (models.Q(app_label = 'YourApp', model = 'ProductionOrder') |
models.Q(app_label = 'YourApp', model = 'SalesOrder ') |
models.Q(app_label = 'YourApp',model = 'ProductionOrder '))
content_type = models.ForeignKey(ContentType, limit_choices_to = limit)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
See the docu
If you want them to integrate nicely into the admin you should also consider using django-smart-selects.

Related

bulk create in django with foreign key

Models:
class Author(Base):
name = models.CharField(max_length=100, unique=True)
class Book(Base):
name = models.CharField(max_length=100, unique=True)
class AuthorBookAssn(Base):
author = models.ForeignKey(Author, on_delete=models.PROTECT)
book = models.ForeignKey(Book, on_delete=models.CASCADE)
I have an api to create a book, and along with the book data we would also get a list of author ids.
Now for each book we need to create one/more records (depending on the author ids provided) in the AuthorBookAssn table.
What is the best way to do this and can the create be done in bulk.
Currently the approach is to get the author objects for each of the ids in the list and then call
AuthorBookAssn.objects.create(book=book_instance,author=author_instance)
You've created a many-to-many relationship so your current method is the only possible way based on your current structure. If you were to use Django's in-built m2m field then you would essentially do the same except you would do something like author.books.add(book), but again, you would have to do this separately to your book/author creation. An alternative would be to use a many-to-one relation (i.e. ForeignKey field) which would allow you to connect the two when an object is created. Many-to-One might not be how you want to structure things if books can have multiple authors and vice-versa.
(supplementary to OsVoid's answer)
There might be some degree of optimization by working with the object ids (primary key values) rather than fetching the entire objects. Premature optimization is a bad idea, and you'd have to benchmark this idea to see if any improvement is measurable (assuming you have any need to optimize at all).
Given book_pk and author_pk you can use the "magic" _id suffix:
AuthorBookAssn.objects.create(book_id=book_pk,author_id=author_pk)
And instead of fetching whole objects, you might fetch just their pk values using a .values_list('pk') in a queryset. (with flat=True if only the one value is being requested). Since this is just a number, it also might be possible to attach it to some other objects that you really do need to obtain, using annotation.
Also, you can cause your own model to be used for the association in a Django ManyToMany relation, using "through". This is valuable if you want to store extra information about the association, such as when it was created, who by, for what purpose, etc.

Django ManyToMany generic "through" model

I'm writing a gallery field. The field subclasses ManyToManyField and adds its own ajax widget and stuff. I want to make this solution as compact as possible (I mean - I want to write as little code to reimplement this in another projects if possible).
I've decided to create an intermediate table (that provides a 'through' parameter to ManyToManyField), which will hold ordering information:
class IntermediateModel(models.Model):
from_content_type = models.ForeignKey(ContentType)
from_object_id = models.PositiveIntegerField()
from_content_object = generic.GenericForeignKey('from_content_type', 'from_object_id')
to_content_type = models.ForeignKey(ContentType)
to_object_id = models.PositiveIntegerField()
to_content_object = generic.GenericForeignKey('to_content_type', 'to_object_id')
order = models.PositiveIntegerField()
The following questions arise:
Is it possible to have a "through" model for m2m in django having both foreign keys pointing to a generic relations (like the one above)? If so - how to achieve this?
If it's possible to do this - can such model hold generic relations between more than one m2m field? Like: Class <-> Intermediate <-> Student, Gallery <-> Intermediate <-> Photo - both using Intermediate as 'through' model??
EDIT: just tested - I can ;) Can I use abstract classes with 'through' tables? I figured out - if the above mentioned complex scenario won't work I could just create two abstract classes that provide ordering and some other stuff and then always create normal subclasses to actually build some relations :)
If the difference between the intermediate models is just the way you handle them, maybe you just need to use it with "proxy" models. I mean, using Django model subclassing with a Meta option called "proxy" as True. This way, you can handle them separately, but having them stored in the same database table (if that is an option to your needs).
Read this. Maybe that is what you want. Instead of having 2 database tables with the same structure, you can have 1 table with 2 (or more) ways of accessing and handling them.

unique_together foreign key object properties

I've got two models: Common and ARecord. ARecord has a ForeignKey relationship to Common. I want to ensure that ARecord is unique with a combination of items from ARecord and Common.
class Common(models.Model):
NAIC_number = models.CharField(max_length=5)
file_location_state = models.CharField(max_length=2)
file_location_code = models.CharField(max_length=2)
class ARecord(models.Model):
common = models.ForeignKey(Common)
coverage_code = models.CharField(max_length=6)
record_type = models.CharField(max_length=1)
class Meta:
unique_together = ('coverage_code', 'common__NAIC_number')
However, when I attempt to access the foreign key object property via the usual double underscore, I get a model validation error.
`arecord.arecord: "unique_together" refers to common__NAIC_number, a field that doesn't exist. Check your syntax.`
This seems like it should be possible and, a slightly different question was asked that indicates it is , but perhaps I'm missing something obvious?
As Manoj implies, you can't do this with unique_together, because that is a database constraint and the sort of thing you want can't be done with database constraints.
Instead, you want do this programmatically, probably via model validation, which will ensure that no instances are created that violate your constraint.
This doesn't make sense to me. The documentation defines unique_together thus:
This is a list of lists of fields that must be unique when considered together. It's used in the Django admin and is enforced at the database level (i.e., the appropriate UNIQUE statements are included in the CREATE TABLE statement).
(Emphasis added)
I don't know how an UNIQUE statement can be added at the database level for such a case (using one column in the current table and another in a different table accessed through a foreign key). I hope those who know better about databases will correct me if I am wrong.

How to set up Django admin.TabularInline on a table with a compount key

I've essentially got two tables: Page(PK=url) and PageProperty(PK=url+name).
Here is how I have my Models set up:
class Page(model.Model):
url = model.CharField(primary_key=True, max_length=255, db_column='url')
#.....
class PageProperty(model.Model):
# table with compound key (url + name)
url = model.ForeignKey('Page', to_field='url', db_column='url', primary_key=True)
name = model.CharField(primary_key=True, max_length=20)
value = model.TextField()
I have a ModelAdmin set up so I can Inline edit PageProperty(s) from Page. Its a legacy database and I know there's a lot of data in there. But the Admin is only showing ONE of the PagePropertys, not all.
I think you might need to apply the extra option to your TabularInline. Example:
class PagePropertyInline(admin.TabularInline):
model = PageProperty
extra = 3
You could probably do some magic to make the amount of extra items dynamic (such as the number of PageProperty objects for a given Page, but I'll leave that up to you.
I would suggest further reading on InlineModelAdmin options and Formsets.
Because it felt as thought a non-integer primary key was too much against the grain, I ended up buckling down and migrating the schema to use an auto generated integer pk for both tables. After that everything was smooth sailing again and the Inlines worked perfectly.

How to store arbitrary name/value key pairs in a Django model?

I have a fixed data model that has a lot of data fields.
class Widget(Models.model):
widget_owner = models.ForeignKey(auth.User)
val1 = models.CharField()
val2 = models.CharField()
...
val568 = ...
I want to cram even more data into this Widget by letting my users specify custom data fields. What's a sane way to do this? Is storing name/value pairs where the user can specify additional "Widget fields" a good idea? My pseudo thoughts are below:
data_types = ('free_text', 'date', 'integer', 'price')
class CustomWidgetField(models.Model)
owner = ForeignKey(auth.User)
field_title = models.CharField(auth.User)
field_value_type = models.CharField(choices = data_types)
class CustomWidgetValue(models.Model)
field_type = ForeignKey(CustomWidgetField)
widget = ForeignKey(Widget)
value = models.TextField()
So I want to let each user build a new type of data field that will apply to all of their widgets and then specify values for each custom field in each widget. I will probably have to do filtering/searching on these custom fields just as I would on a native field (which I assume will be much slower than operating on native fields.) But the scale is to have a few dozen custom fields per Widget and each User will only have a few thousand Widgets in their inventory. I can also probably batch most of the searching/filtering on the custom fields into a backend script (maybe.)
Consider representing all custom properties with serialized dict. I used this in a recent project and it worked really well.
class Widget(models.Model):
owner = models.ForeignKey(auth.User)
props = models.TextField(blank=True) # serialized custom data
#property
def props_dict(self):
return simplejson.loads(self.props)
class UserProfile(models.Model)
user = models.ForeignKey(auth.User)
widget_fields = models.TextField(blank=True) # serialized schema declaration
It looks like you've reinvented the triple store. I think it's a common thing, as we follow the idea of database flexibility to its natural conclusion. Triple stores tend to be fairly inefficient in relational database systems, but there are systems designed specifically for them.
http://en.wikipedia.org/wiki/Triplestore
At the scales you're talking about, your performance is likely to be acceptable, but they don't generally scale well without a specialized DB.
In my opinion, the best way to achieve this sort of completely extensible model is really with EAV (Entity, Attribute, Value). Its basically a way to bring a schemaless non-relational database to SQL. You can read a bunch more about it on wikipedia, http://en.wikipedia.org/wiki/Entity-attribute-value_model but one of the better implementation of it in django is from the EveryBlock codebase. Hope it's a help!
http://github.com/brosner/everyblock_code/blob/master/ebpub/ebpub/db/models.py
http://github.com/tuttle/django-expando may be of interest to you.
When I had an object that could be completely customized by users, I created a field on the model that would contain some JSON in the column. Then you can just serialize back and forth when you need to use it or save it.
However, it does make it harder to use the data in SQL queries.