Django: Same model with different features? - python

Object Model:
class Object(models.Model):
author = models.ForeignKey(ProfileUser, on_delete=models.CASCADE)
title = models.CharField(max_length=300)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
address = models.CharField(max_length=300)
content = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
approved_object = models.BooleanField(default=False)
admin_seen = models.BooleanField(default=False)
def __str__(self):
return f"{self.title}"
Category model:
class Category(models.Model):
title = models.CharField(max_length=50)
def __str__(self):
return f"{self.title}"
For example I have some categories, like hotels, restaurants etc. So I want for each category to have different features (radio buttons when adding new), but I'm not sure, how to handle it properly. Hotels must have rooms, pool etc. Restaurants must have country kitchen, seats etc. In future I will have and other categories.
Quesion is: Which is the best way (practice) to do this.
My solution: To create third table with features and every category to have column features and store features separate by comma, but it's not very good solution based on DB normalization.

You could use abstract base classes:
Abstract base classes are useful when you want to put some common information into a number of other models. You write your base class and put abstract=True in the Meta class. This model will then not be used to create any database table. Instead, when it is used as a base class for other models, its fields will be added to those of the child class.
You can then make your Restaurant and Hotel models inherit from this abstract class. Django will then create two tables with the base fields from the abstract class plus the specific fields from each models.

You can use multi-table inheritance
You define a base object, and from there you can define different child objects which will share the parent's properties
In your case that would look something like this:
class Object(models.Model):
...
class Restaurant(Object):
seats = IntegerField(...)
reservations = ManyToManyField(...)
class Hotel(Object):
rooms = IntegerField(...)
has_pool = BooleanField(...)
Django will automatically create relationships and manage the querying for you. To get all restaurants, you can use Restaurant.objects.all(), there is a limitation though. When querying Object.objects.all(), you will get a list of the Objects, not their specific subclass. If I remember correctly, you can access the specific instance through (for instance) object.restaurant.
If you do want to get the specific objects, you can look into a library called Django-polymorphic.

i will explain this one by one :
category table contains all categories.
now there may have some common and unique feature for every category.features will have many to many relation with category table.
so we create feature_master table and we will map it with category table.
feature_master table contains all features.
category_feature_map table is map table(junction table)
object table have all the detail about object and object_detail table will contain all the feature to particular object

Related

Django models: Inherit from variable abstract base class

I'm hoping to inherit to a child class from a variable abstract base class. So a child class would not have to inherit from a pre-defined base class and would instead be able to inherit from any one class of multiple base classes. Ideally, the models would be setup like so:
class Orders(models.Model):
order_number = models.IntegerField()
# Orders metrics
class Meta:
abstract = True
class Fees(models.Model):
fee_number = models.IntegerField()
# Fee metrics
class Meta:
abstract = True
class Transactions(Inherit from either Orders or Fees):
transaction_number = models.IntegerField()
# Transaction metrics
Transactions would be able to inherit from either orders or fees as they could both be a source of a transaction.
Generic foreign keys could be implemented to allow for variable foreign key reference within the Orders model and Fees model but I am curious if there is a way to do this without using generic foreign keys. Is there a specific arrangement, mixin, decorator, property, or method that will allow for association of a child class with a variable abstract parent class?
This is not possible. But what you want can be easily achieved by creating ForeignKey realtionships from Transaction to both Fees and Order.
No you can't do this. Django models can't be modified like this during runtime (after django is intialized). Anyway, this is not a good design pattern. You're confusing composition and inheritance. A Transaction is not a type of Fee or a type of Order so it makes no sense to subclass like this.
You can solve your problem without a generic foreign key by just using two separate ForeignKey fields:
class Transactions(models.Model):
transaction_number = models.IntegerField()
order = models.ForeignKey(Orders, null=True, blank=True)
fee = models.ForeignKey(Fees, null=True, blank=True)
You can then query the different transaction types like this:
fee_payments = Transactions.objects.exclude(fee=None)
order_payments = Transaction.objects.exclude(order=None)

Best practices for products catalogue in Django

I'm working on a catalogue application for storing product information in Django.
The challenge here is that there are a lot of products in the catalogue, each with their own attributes. Since this is a key part of the application, and a lot will be built around it, I want this to be designed as good as possible.
I know there are several options of handling this in Django, but I would like to know what others have experienced with these several options.
The first option: Single abstract class
I could just create a single abstract class, which contains all the base attributes, and let other classes derive from that one class.
class Product(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(max_length=1000)
price = models.DecimalField()
class Meta:
abstract = True
class Phone(Product):
series = models.CharField(max_length=100)
This would be the most straightforward option, but this will include a lot of work in Django Forms and Views.
This will also create a single table for each Product subclass, so when the Product class is changed, all other tables will have to changed as well.
The second option: Product base class
Here the Product class is not abstract, which implies this class can be used as well.
class Product(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(max_length=1000)
price = models.DecimalField()
class Phone(Product):
series = models.CharField(max_length=100)
This would also be pretty straightforward, but this would still imply a lot of work in the Forms and Views.
This would first create a table for the Product class, and then a single table for each Product subclass.
The first 2 options will also break the DRY principle, because attributes will have to be added to every Product subclass that might be common to some classes, but not to all.
Third option: Product class containing all the possible attributes.
class Product(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(max_length=1000)
price = models.DecimalField()
# attributes for phones, tv's, etc...
series = models.CharField(max_length=100)
class PhoneForm(forms.ModelForm):
class Meta:
model = Product
fields = ['name', 'description', 'price', 'series']
A new form will have to be created for each product subclass. This does seem pretty easy, but the Product model will become very bloated.
In this case I could also use Proxy Models.
Fourth option: Create Abstract classes and use class Mixins
class ProductBase(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(max_length=1000)
price = models.DecimalField()
class Meta:
abstract = True
class ElectronicsProduct(models.Model):
series = models.CharField(max_length=100)
class Meta:
abstract = True
class Phone(ProductBase, ElectronicsProduct):
pass
This method could solve the DRY problem I have with the issue I had above, but still not optimal.
Fifth option: One Product model with a separate attribute model
This is a method I would like to use anyway, but more to have the ability to add 'extra' features to a product that is too specific to put in a Product or Product subclass.
class Product(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(max_length=1000)
price = models.DecimalField()
class Attribute(models.Model):
name = models.CharField(max_length=100)
class ProductAttribute(models.Model):
product = models.ForeignKey(Product)
attribute = models.ForeignKey(Attribute)
value = models.CharField(max_length=100)
The question here is if this method should be used for all product attributes, since I think this will add a lot of overhead on the database.
Another challenge here is the value type. In this case I can only use a character value, so what happens when I would like to use a Decimal value, or a File.
Sixth option: Something else
There are probably some methods I have not thought of at this point. So if you know something I don't please share it with me.
I am not looking for any opinions here, but for some solutions. So if you have an answer to this question please tell us why you would use the method you propose.

Django Model field needs to be unique over multiple models with shared abstract base class

Suppose I want to have multiple kinds of articles, all reachable on site.com/news/article-slug
(so depending on article-slug, it can be a video article, a text article, an image slider article, ...)
I have these models:
class Article(models.Model):
class Meta:
abstract = True
slug = models.SlugField(max_length=60, unique=True)
class TextArticle(Article):
content = models.TextField()
class VideoArticle(Article):
video = models.ForeignKey(Video)
But the problem is that I can create a TextArticle and a VideoArticle with the same slug.
Is there an easy way to fix it so that if there's already a VideoArticle with a specific slug, no TextArticle can be added with the same slug (and vice versa)?
If there's no easy fix:
should I go for custom form validation with database querying?
should I not make the base class abstract?
any other ideas?
Thanks in advance!
It sounds like you don't actually want an abstract class, but rather for the Article class to be the table for which all subclasses reference:
https://docs.djangoproject.com/en/1.10/topics/db/models/#multi-table-inheritance

Django Generic Foreign Key Sorting

So I have a model that has a generic foreign key relationship with with three other models
class Status(BaseRequestStatus):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
class Request(models.Model):
name = models.CharField(max_length=50, db_index=True)
...some fields
statuses = GenericRelation(Status, related_query_name='request')
class AnotherRequest(models.Model):
name = models.CharField(max_length=50, db_index=True)
...some fields
statuses = GenericRelation(Status, related_query_name='another_request')
class ThirdRequest(models.Model):
name = models.CharField(max_length=50, db_index=True)
...some fields
statuses = GenericRelation(Status, related_query_name='third_request')
So currently I am displaying the statuses with the name in a table. I want to be able to sort by the name. Currently I am sorting it like this.
Status.objects.all().order_by('request__name', 'another_request__name', 'third_request__name')
however with this method all of the 'request' is sorted in a chunk, then 'another_request', then 'third_request'
is there anyway to sort all of these together? I was able to do it by converting it to a list.. but in this case I need to use a Queryset.
Thanks in advance for any help.
If you want them treated the same way (deducing this from the fact that you want them sorted by their common name field) then this indicates that these models have the same base.
Use model inheritance in that case with a base model that is abstract and defines the name field and the generic fkey relation.
class RequestBase(models.Model):
name = models.CharField(max_length=50, db_index=True)
statuses = GenericRelation(Status, related_query_name='request')
class Meta:
abstract = True
class Request(RequestBase):
# ... more fields
Status.objects.all().order_by('request__name')
A more general explanation: to be able to sort objects by one attribute, you have to make clear that they all share that same attribute. Either by fetching the values in your own routines and creating lists that are sortable (this would be the qs approach you mentioned) or by creating a common interface - which is this Model inheritance approach.
As long as you are not using table inheritance, though, you will not be able to use the database for sorting (meaning: you cannot use the Django ORM for sorting).
There are certainly other approaches, including those that are outside of Django models: for example generating an additional lookup structure like a search index which will contain your transformed data as required (e.g. a haystack index).

Hardcoding attributes in django models

OK here goes, this is one of those questions that makes perfect sense in my head but is difficult to explain properly :) I have a django app where I want to store records for lots of different items of equipment. Each type of equipment will have a custom model to store its attributes, such as MyEquipment below. Each type of equipment will also have a 'category', which would be useful to store as an attribute.
class Category(models.Model):
code = models.CharField('Category', max_length=4, unique=True)
description = models.CharField('Description', max_length=30)
...
class MyEquipment(models.Model):
serial = models.IntegerField()
...
To save this attribute to my model I could use a foreign key to Category but I don't need to because every record in MyEquipment must be the same Category. So then I thought maybe I could hardcode the Category in the MyEquipment meta like this:
class MyEquipment(models.Model):
serial = models.IntegerField()
...
class Meta:
category = Category.objects.get(code='EC')
But then this would rely on the Category model being populated with data to build the MyEquipment model. To me this doesn't seem best practice, using data that may or may not exist to define the structure of another model. Is there a better way I should be using to set which Category the MyEquipment model is related to?
EDIT
Thanks for the discussion below, it's made me realise perhaps I wasn't clear on my original post. So what I want to do is have a way of linking MyEquipment to a Category. So I can do something like this:
>>> from myapp.models import MyEquipment
>>> MyEquipment.CATEGORY
<Category: EC>
I want to link the whole model to a Category, so I can process each model in different ways in my view depending on which category it is. Having thought about the problem a bit more, I can get this functionality by writing MyEquipment like this:
class MyEquipment(models.Model):
CATEGORY = Category.objects.get(code='EC')
serial = models.IntegerField()
...
This way works, but is it the best way? I guess the model would do this get operation everytime the class is instantiated? Is there a more efficient method?
You can't do this anyway; the Meta class doesn't support arbitrary attributes.
The best thing would be to define this as a property, which you can access via the instance itself. To make it more efficient, you could memoize it on the class.
#property
def category(self):
_category = getattr(self, '_category', None)
if not _category:
self.__class__._category = _category = Category.objects.get(code='EC')
return _category
but ... every record in MyEquipment must be the same Category
Then you don't need any relationship. As you said already, every record in MyEquipment are same Category, why do you want to store relation in db?
UPD: Solution with model inheritance
class Place(models.Model):
category = models.ForeignKey(Category)
class Meta:
abstract = True
def save(self, *args, **kwargs):
self.category = Category.objects.get(name=self.CATEGORY)
return super(Place, self).save(*args, **kwargs)
class Restaurant(Place):
...fields...
CATEGORY = 'RE'
class Building(Place):
...fields...
CATEGORY = 'BU'

Categories