How to get 2 different classes in 1 model field in django? - django

I am working in a project, where I have to merge/combine different classes into one field.
I want to do it that way, because I have two different classes ParkingArea1 and ParkingArea2 and I want to have the choice from the Admin and WebInterface, to select any of these two areas from a single drop-down list, and ideally store them in another's model field.
These are my models:
class ParkingArea1(models.Model):
zone_name = models.Charfield(max_length=255, unique=True)
# some more unique fields below
def __str__(self): return self.zone_name
class ParkingArea2(models.Model):
area_name = models.CharField(max_length=200, db_index=True, null=True, unique=True)
# some more unique fields below
def __str__(self): return self.area_name
class ChooseYourArea(models.Model):
area = # here I want a field, which should be able to hold any of the above classes
# some other stuff
How should I reform my models to achieve that?
NOTE: ParkingArea1 and ParkingArea2 are two actively used tables filled with data, so a change within these models wouldn't be optimal.
I researched a bit but couldn't find something to fit my case.
I understand that an abstract Base Class could be used for this case, but such a Base Class won't allow me to list all the areas that I want. Is there an obvious approach that I missed?

You can't if you want to use ForeignKeys. Both ParkingArea1 and ParkingArea2 have their own primary keys. Django won't know which table that foreign key belongs to. You need 2 fields.
There are ways around that but you need to ensure data consistency at the Django level, and the performance would be abysmal.

What I ended up doing, was to create another class ParkingAreas with two fields, where each field can hold either ParkingArea1 or ParkingArea2:
class ParkingAreas(models.Model):
area1 = models.OneToOneField(ParkingArea1, on_delete=models.SET_NULL, blank=True, null=True, default=None)
area2 = models.OneToOneField(ParkingArea2, on_delete=models.SET_NULL, blank=True, null=True, default=None)
def __str__(self):
if self.area1 is not None:
return self.area1.area1
else:
return self.area2.area2
class ChooseYourArea(models.Model):
area = models.ForeignKey(ParkingAreas, on_delete=models.SET_NULL, blank=True, null=True)
# some other stuff
Now I can get all parking areas in one place, even selectable with a drop-down menu, and store them in a ForeignKey field.
That way, when I need to access the areas, I can make some django queries and get what I need.

Related

How to define models in django that provides custom values based on pre-selection for a field?

Given the following model that stores the user's wish list for reading books:
class ReadingList(models.Model):
user_id = models.ForeignKey(UserInfo, on_delete=models.DO_NOTHING, null=False, blank=False, default=None, db_column='user_id')
book= models.CharField(max_length=255, blank=False)
creation_time = models.DateTimeField(blank=True)
class Meta:
unique_together = (('user_id', book),)
I want to create a model that helps in tracking the time spent in the reading the book on different days which looks something like this:
class ReadingTracker(models.Model):
user_id = models.ForeignKey(ReadingList, on_delete=models.DO_NOTHING, related_name='user', blank=False, db_column='user_id')
book= models.ForeignKey(ReadingList, on_delete=models.DO_NOTHING, related_name='book-to-read', blank=False, db_column='book')
time = models.DateTimeField(blank=True)
time_spent = models.floatfield()
On the client-side (corresponding to ReadingTracker) for both the fields user_id and book
I see that ReadingList object (1), ReadingList object (2), ... are listed. But, this is not working as expected.
What I want to achieve are the following:
For user_id field I want to see the something like dummy_uid1, dummy_uid2, ... to be listed.
Consider dummy_uid1 wants to read book1 and book2 whereas dummy_uid2 wants to read book1 and book3.
When dummy_uid1 is selected as user_id, I want only book1 and book2 to be listed for selection.
How do I define the model in django rest framework to achieve this?
Any suggestions related to the above would be much appreciated and thank you in advance.
There are two parts to this question:
If you want to see a different value than ReadingList object (1) then you need to define the __str__ value of your model, you can do this like so:
class ReadingList(models.Model):
...
def __str__(self):
return f'{self.user_id}' # return whatever string you want to display
If you want to just display the books for a particular user then you can use a filter() (see the Django documentation):
reading_list = ReadingList.objects.get(...)
ReadingTracker.objects.filter(user_id=reading_list)
However, I would add that you have a user_id on your ReadingList object which does seem to connect to a User model, but your user_id on ReadingTracker is a ForeignKey relation to ReadingList, which is confusing. I would suggest renaming the field or actually making it link to the User model (though this is unnecessary as you can still filter by User through the ReadingList model).

Django one to many models - How to create the "many" when the "one" is created?

There are a lot of questions along these lines, but so far as I can tell, but many are quite old and I haven't found one that helped me understand my use case. I think I want to be looking at signals, but I'm not clear on exactly what I should be doing in terms of Django patterns and best practices.
Here's my Model code:
from django.db import models
from django.db.models.fields import SlugField, TextField
from django.utils import timezone
# Hexes have Terrains, which impart intrinsic properties, like base speed and .png image
# They're NOT 1:1 with Hexes IE) You might want Slow and Normal Grasslands with the same image
class Terrain(models.Model):
FAST = 'F'
NORMAL = 'N'
SLOW = 'S'
SPEED_CHOICES = [
(FAST, 'Fast'),
(NORMAL, 'Normal'),
(SLOW, 'Slow'),
]
name = models.CharField(max_length=64)
speed = models.CharField(max_length=6, choices=SPEED_CHOICES, default=NORMAL)
image = models.ImageField(upload_to='images/', blank=True)
def __str__(self):
return self.name
# Grids establish the dimensions of a HexMap. Grids may be used in many HexMaps
class Grid(models.Model):
name = models.CharField(max_length=64)
size = models.IntegerField(default=72)
num_rows = models.IntegerField(default=10)
num_cols= models.IntegerField(default=10)
def __str__(self):
return self.name
# Hexes represent the "tiles" on a Grid. A single Hex may appear in many Grids
class Hex(models.Model):
name = models.CharField(max_length=64, blank=True, null=True)
terrain = models.ForeignKey(Terrain, on_delete=models.CASCADE)
grids = models.ManyToManyField(Grid)
def __str__(self):
return self.id
class Meta:
verbose_name_plural = "Hexes"
# Locations are coordinal points on a HexMap. They may contain many Annotations
class Location(models.Model):
# HexMap Name + row + col, for a consistent way to ID locations
name = models.CharField(max_length=64, blank=True, null=True)
friendly_name = models.CharField(max_length=64, blank=True, null=False)
# TODO constrain these to the sizes specified in the grids? Sane defaults?
row = models.PositiveIntegerField()
col = models.PositiveIntegerField()
# Authors create Annotations
# TODO create Players and make Authors extend it so we can do rights and perms
class Author(models.Model):
first_name = models.CharField(max_length=64, blank=False, null=False)
last_name = models.CharField(max_length=64, blank=False, null=False)
join_date = timezone.now()
def __str__(self):
return self.first_name + " " + self.last_name
# Annotations are entries created by Authors. A location may have many Annotations
class Annotation(models.Model):
name = models.CharField(max_length=64, blank=False, null=False)
pubdate = join_date = timezone.now()
content = TextField()
# Hexmaps put it all together and allow fetching of Locations by (r,c) coords
class HexMap(models.Model):
name = models.CharField(max_length=64, blank=True, null=True)
rows = {}
cols = {}
#TODO - how do I create grid.num_rows * grid.num_cols locations and put them into the dicts?
I am probably making a ton of mistakes as I've not worked in Django in close to a decade. Any constructive feedback is appreciated in the comments. Most of the relationships are not coded in, as I'm not sure how they should look.
My specific question, however, is related the HexMap and Location objects.
When a HexMap is created, I want to create a set of empty Locations with a default name value matching a naming convention like HexMap.name + row + col and fill the row{} and col{} dicts of the new HexMap with them.
My instinct is that a Hexmap needs a Grid in order to be created, so I could create a nested loop inside HexMap with grid.num_rows as as the outer loop and num_cols as the inner loop. Each iteration through the inner loop builds a row of Locations as a dict. Each iteration of the outer loop adds that row to the rows{} dict. One thing I'm not certain about is how I'd get the grid.num_rows and grid.num_cols when the HexMap isn't even created yet, and doesn't have a grid associated.
Is this anywhere near the right way to do this in Django, or am I off the mark? Many of the answers and online tutorials seem to indicate that I need to look at signals, but I'm just not really clear on whether or not that's the case. Also, if so, I could use some similar examples to review. The Django docs are great and helpful, but when you're "new" to it all, it can be hard to parse and adopt the examples.
Found this question, which is the question I really should have asked in the first place. While a few years old, I think it's still the right answer and helped direct me to the appropriate documentation. Still not sure what my next step is, thinking I need some sane defaults and an override of the save method to use those if the instances of the other fields don't exist yet. I'm still working out wether or not I have a valid use case for signals here. I think that I will eventually when I add more apps to the project and actions in those apps need to interact with the models in this app.

Best way to handle one ForeignKey field that can be sourced from multiple Database Tables

I am running into a little bit of unique problem and wanted to see which solution fit best practice or if I was missing anything in my design.
I have a model - it has a field on it that represents a metric. That metric is a foreign key to an object which can come from several database tables.
Idea one:
Multiple ForeignKey fields. I'll have the benefits of the cascade options, direct access to the foreign key model instance from MyModel, (although that's an easy property to add), and the related lookups. Pitfalls include needing to check an arbitrary number of fields on the model for a FK. Another is logic to make sure that only one FK field has a value at a given time (easy to check presave) although .update poses a problem. Then theres added space in the database from all of the columns, although that is less concerning.
class MyModel(models.Model):
source_one = models.ForeignKey(
SourceOne,
null=True,
blank=True,
on_delete=models.SET_NULL,
db_index=True
)
source_two = models.ForeignKey(
SourceTwo,
null=True,
blank=True,
on_delete=models.SET_NULL,
db_index=True
)
source_three = models.ForeignKey(
SourceThree,
null=True,
blank=True,
on_delete=models.SET_NULL,
db_index=True
)
Idea two:
Store a source_id and source on the model. Biggest concern I have with this is needing to maintain logic to set these fields to null if the source is deleted. It otherwise seems like a cleaner solution, but not sure if the overhead to make sure the data is accurate is worth it. I can probably write some logic in a delete hook on the fk models to clean MyModel up if necessary.
class MyModel(models.Model):
ONE = 1
TWO = 2
THREE = 3
SOURCES = (
(ONE, "SourceOne"),
(TWO, "SourceTwo"),
(THREE, "SourceThree")
)
source_id = models.PositiveIntegerField(null=True, blank=True)
source = models.PositiveIntegerField(null=True, blank=True, choices=SOURCES)
I would love the communities opinion.
Your second idea seems fragile as the integrity is not ensured by the database as you have pointed out yourself.
Without knowing more about the use case, it's difficult to provide an enlightened advice however if your "metric" object is refered by many other tables, I wonder if you should consider approaching this the other way round, i.e. defining the relationships from the models consuming this metric.
To exemplify, let's say that your project is a photo gallery and that your model represents a tag. Tags could be associated to photos, photo albums or users (e.g.. the tags they want to follow).
The approach would be as follow:
class Tag(models.Model):
pass
class Photo(models.Model):
tags = models.ManyToManyField(Tag)
class Album(models.Model):
tags = models.ManyToManyField(Tag)
class User(AbstractUser):
followed_tags = models.ManyToManyField(Tag)
You may even consider to factor in this relationship in an abstract model as outlined below:
class Tag(models.Model):
pass
class TaggedModel(models.Model):
tags = models.ManyToManyField(Tag)
class Meta:
abstract = True
class Photo(TaggedModel):
pass
As mentioned in the comments, you are looking for a Generic Relation:
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
class SourceA(models.Model):
name = models.CharField(max_length=45)
class SourceB(models.Model):
name = models.CharField(max_length=45)
class MyModel(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
source = GenericForeignKey('content_type', 'object_id')
There are three parts to setting up a Generic Relation:
Give your model a ForeignKey to ContentType. The usual name for this field is “content_type”.
Give your model a field that can store primary key values from the models you’ll be relating to. For most models, this means a PositiveIntegerField. The usual name for this field is “object_id”.
Give your model a GenericForeignKey, and pass it the names of the two fields described above. If these fields are named “content_type” and “object_id”, you can omit this – those are the default field names GenericForeignKey will look for.
Now you can pass any Source instance to the source field of MyModel, regardless of which model it belongs to:
source_a = SourceA.objects.first()
source_b = SourceB.objects.first()
MyModel.objects.create(source=source_a)
MyModel.objects.create(source=source_b)

Which pattern to use for a model to have different variations of? Generic FK, multi-table, inheritance, others?

I am having trouble deciding how to structure my models for a particular data structure.
The models I have would be Posts, Groups, Users.
I want the Post model that can be posted from a groups page or user page and potentially more, like an events page.
Posts would contain fields for text, images(fk), user, view count, rating score (from -- a reference to where ever it was posted from like user or group page, though I am unsure how to make this connection yet)
I thought about using a Generic Foreign Key to assign a field to different models but read articles suggesting to avoid it. I tried the suggested models, but I wasn't unsure if they were the right approach for what I required.
At the moment I went with Alternative 4 - multi-table inheritance
class Group(models.Model):
name = models.CharField(max_length=64)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='_groups')
members = models.ManyToManyField(
settings.AUTH_USER_MODEL)
def __str__(self):
return f'{self.name} -- {self.created_by}'
def save(self, *args, **kwargs):
# https://stackoverflow.com/a/35647389/1294405
created = self._state.adding
super(Group, self).save(*args, **kwargs)
if created:
if not self.members.filter(pk=self.created_by.pk).exists():
self.members.add(self.created_by)
class Post(models.Model):
content = models.TextField(blank=True, default='')
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name="%(app_label)s_%(class)s_posts",
related_query_name="%(app_label)s_%(class)ss")
# class Meta:
# abstract = True
def __str__(self):
return f'{self.content} -- {self.created_by}'
class PostImage(models.Model):
image = models.ImageField(upload_to=unique_upload)
post = models.ForeignKey(
Post, related_name='images', on_delete=models.CASCADE)
def __str__(self):
return '{}'.format(self.image.name)
class UserPost(models.Model):
post = models.OneToOneField(
Post, null=True, blank=True, related_name='_uPost', on_delete=models.CASCADE)
class GroupPost(models.Model):
post = models.OneToOneField(
Post, null=True, blank=True, related_name='_gPost', on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
To do some specific filters ex:
Filter specific group post
Post.objects.filter(_gPost__group=group)
Filter specific user post
Post.objects.filter(created_by=user) # exclude groups with _gPost__isnull=False
Create post to user/group
p = Post.objects.create(...)
up = UserPost.objects.create(post=p)
gp = GroupPost.objects.create(post=p)
Really I am wondering if this is a sensible approach. The current way of a filter and creating feel odd. So only thing making me hesitant on this approach is just how it looks.
So, is Generic ForeignKey the place to use here or the current multi-table approach. I tried going with inheritance with abstract = True and that was unable to work as I need a foreign key to base post model. Even with no abstract, I got the foreign key reference, but filter became frustrating.
Edit:
So far only weird issues(but not really) are when filtering I have to be explicit to exclude some field to get what I want, using only .filter(created_by=...) only would get all other intermediate tables.
Filter post excluding all other tablets would requirePost.objects.filter(_uPost__isnull=True, _gPost__isnull=True, _**__isnull=True) which could end up being tedious.
I think your approach is sensible and that's probably how I would structure it.
Another approach would be to move the Group and Event foreignkeys into the Post model and let them be NULL/None if the Post wasn't posted to a group or event. That improves performance a bit and makes the filters a bit more sensible, but I would avoid that approach if you think Posts can be added to many other models in the future (as you'd have to keep adding more and more foreignkeys).
At the moment I will stick with my current pattern.
Some extra reading for anyone interested.
https://www.slideshare.net/billkarwin/sql-antipatterns-strike-back/32-Polymorphic_Associations_Of_course_some

Django model configuration for dynamic attributes

I have been trying to figure out the best way (or correct) way to set up models for our PIM/PriceModel app in Django.
Example models (stripped):
class ProductItem(models.Model):
"""
An actual item/product, what it all revolves around.
"""
part_number = models.CharField(unique=True, max_length=50, help_text='')
internal_part_number = models.CharField(primary_key=True, max_length=50, help_text='') # prefilled by partnumber
type = models.ForeignKey('Type', null=True, on_delete=models.SET_NULL)
attributes = JSONField() # Or another dynamic field
# and more ....
class Type(models.Model):
"""
Product type i.e. camera-dome, camera-bullet, pir, etc.
"""
pass
class Segment(models.Model):
"""
A segment of the company like Industry, Retail, Guarding etc.
"""
pass
class ProductCategory(models.Model):
"""
Supposedly a child category of TopCategory.
"""
pass
class InstallPrice(models.Model):
"""
Product item installation prices based on Type, Product Category and Segment.
"""
install_price = models.DecimalField(max_digits=8, decimal_places=2, help_text='')
type = models.ForeignKey('Type', null=True, on_delete=models.SET_NULL)
product_category = models.ForeignKey('ProductCategory', null=True, on_delete=models.SET_NULL)
segment = models.ForeignKey('Segment', null=True, on_delete=models.SET_NULL)
Take a look at "attributes = HStoreField(db_index=True)" in the ProductItem model.
The main thing i need to store in a product item is attributes like how many inputs/outputs/connection-options does it have. I need to store this for testing products against each other further down the line in the price-model app. This to make sure you have the right amount of products with matching attributes like inputs or outputs. I also need the User/Admin to be able to add this attributes dynamically so the app becomes self sustainable and not requires a migration id there is a new attribute I dont yet know about.
As I could not figure out a reasonable model configuration I ended up looking at postgres specific fields. This is not a must!
ideally when selecting type in the admin section i would like a "preset" of attributes to be presented based on the type.
Attributes could be:
inputs # number
outputs # number
channels # number
analysis # Boolean
Is this achievable? Any suggestions are welcome as I have limited Data Base experience. I need help figuring out the models.