Django: need help in designing relationships between models - django

I have an app which allows to associate to each client multiple boards, boards where I can upload files relevant for the client to make decisions about how the website page in question will look.
So the relationships I need to model are:
one client, multiple boards;
one board, one client;
one board, multiple files;
Let's concentrate on the first two
models.py
class Board(models.Model):
title = models.CharField(max_length=120, verbose_name="Titolo")
description = models.TextField()
files = models.FileField( null=True, blank=True, upload_to = 'clients_download_area', verbose_name = 'Client Reserved File')
date = models.DateTimeField(auto_now_add=True, verbose_name = 'Data di pubblicazione')
def __str__(self):
return str(self.title)
class Client(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=120)
boards = models.ManyToManyField(Board, blank=True)
def __str__(self):
return str(self.name)
Ok, relationship #1 is done. But what if I need to know which client is associated to a board (relationship #2)?
If I set a new field to Board Class
class Board(models.Model):
[...]
client = models.ForeignKey(Client, blank = True)
of course, when I makemigrations Django complains because it does not know what Client is, since I define it in the next model.
How can I design this DB?
Thank you in advance for any help you could provide

The problem here is that you reference to an identifer (here Client), before it is constructed (since Board is defined before the Client class).
Django has some support for this: you can pass strings that contain the name of the model. In the same app, you can just use ClassName, for another app, you can use other_app.ClassName, like the documentation says:
If you need to create a relationship on a model that has not yet been defined, you can use the name of the model, rather than the model
object itself:
from django.db import models
class Car(models.Model):
manufacturer = models.ForeignKey(
'Manufacturer',
on_delete=models.CASCADE,
)
# ...
class Manufacturer(models.Model):
# ...
pass
So here you can write it like:
class Board(models.Model):
title = models.CharField(max_length=120, verbose_name="Titolo")
description = models.TextField()
files = models.FileField( null=True, blank=True, upload_to = 'clients_download_area', verbose_name = 'Client Reserved File')
date = models.DateTimeField(auto_now_add=True, verbose_name = 'Data di pubblicazione')
client = models.ForeignKey(
'Client',
related_name='my_boards'
on_delete=models.CASCADE
)
def __str__(self):
return str(self.title)
class Client(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=120)
boards = models.ManyToManyField(Board, blank=True)
def __str__(self):
return str(self.name)
Note however that you already defined a ManyToManyField relation from Client to Board. Although it is possible it is not very common that two such relations exists simultaneously.
If you define a ForeignKey relationship from Board to Client, then Django automatically creates a reverse relationship (with the related_name), such that some_client.my_boards, is a manager of all the related Boards.

I think you can pass model class name instead of the class itself:
class Board(models.Model):
[...]
client_id = models.ForeignKey('Client', blank=True)

Related

Getting a column instead of an object when relating with PrimaryKeyRelatedField in Django-Rest-Framework

I have a model for applications, which among many attributes have a category. This category is in fact a key to another model that has the category ID, its name, and so on.
class Application(models.Model):
title = models.CharField(max_length=50)
vendor = models.CharField(max_length=50, default="Unknown", null=False)
.
.
.
category = models.ForeignKey('ApplicationCategory', related_name='applications', null=False, default=1, on_delete=models.SET_DEFAULT)
class ApplicationCategory(models.Model):
name = models.CharField(max_length=20, null=False)
description = models.CharField(max_length=200, null=False)
Then, on the Django REST serializers side I have the serializer for the applications:
class SoftwareSerializer(serializers.ModelSerializer):
category = serializers.PrimaryKeyRelatedField(queryset=ApplicationCategory.objects.all())
class Meta:
model = Application
fields = ['id', 'title', ... 'category']
Which is generating the expected API view, with a dropdown for the categories, but showing them as the ApplicationCategory objects and not giving me their name.
API showing Category dropdown with objects instead of names
Is there a way to access attributes of those objects to show the name in the dropdown, for usability sake?
I have also tried creating a CategorySerializer object (class CategorySerializer(serializers.ModelSerializer)) and then using it as category = CategorySerializer(many=False) but instead of dropdowns, I get open text fields for the attributes of the category.
Am I trying to do something that is not expected to work?
try to define the desired text in str method for your ApplicationCategory class:
class ApplicationCategory(models.Model):
name = models.CharField(max_length=20, null=False)
description = models.CharField(max_length=200, null=False)
#example
def __str__(self):
return '%s: %s' % (self.name , self.description)

Django - Show only a specific dynamic fields per models in django-eav2

I'm trying to figure it out on how I can show only a specific set of dynamic fields in eav to a unique registered model in my apps.models. But I don't know how to this, I've also read the documents but I can't seem to find anything about it, or maybe I've come across it and didn't understand.
Now, what is happening is that, when I add an attribute in the django admin. It also adds the dynamic field in all the models registered in the eav.
What I want to do is that;
model 1 - dynamic_field1, dynamic_field2, dynamic_field3
model 2 - dynamic_field4, dynamic_field5, dynamic_field6
Btw, I'm currently using the django-eav2 the documentation is in the link. I've found my solution for my initial use case here link
Below codes are basically on how to register my models to the eav. Here is my sample models
class ClientName(models.Model):
name = models.CharField(max_length=250, null=True, blank=True)
description = models.TextField(null=True, blank=True)
is_active = models.BooleanField(default=True)
def __str__(self):
return str(self.name)
class CallDetails(models.Model):
client_name = models.ForeignKey(ClientName, on_delete=models.PROTECT, null=True, blank=True, db_index=True)
letter_info = models.TextField(null=True, blank=True)
def __str__(self):
return str(self.client_name)
class Meta:
verbose_name = 'Call Detail'
ordering = ['client_name']
eav.register(ClientName)
eav.register(CallDetails)
below is my admin.py
class CallDetailsAdminForm(BaseDynamicEntityForm):
model = CallDetails
class CallDetailsAdmin(BaseEntityAdmin):
form = CallDetailsAdminForm
admin.site.register(CallDetails, CallDetailsAdmin)

Proxy model for Multiple models in Django

I would like to display a model in the django admin but with the logic to choose between 2 models to display.
Current Implementation:
Models
class User(models.Model):
created = models.DateTimeField(auto_now_add=True, null=True)
last_updated = models.DateTimeField(auto_now=True)
name = models.CharField(max_length=30, blank=True)
class ExpectedNames(User):
class Meta:
proxy=True`
Admin
#admin.register(ExpectedNames)
class ExpectedNamesAdmin(admin.ModelAdmin):
date_hierarchy = 'created'
What I Would like to DO: # something like this
Models
class User(models.Model):
created = models.DateTimeField(auto_now_add=True, null=True)
last_updated = models.DateTimeField(auto_now=True)
name = models.CharField(max_length=30, blank=True)
class User2(models.Model):
created = models.DateTimeField(auto_now_add=True, null=True)
last_updated = models.DateTimeField(auto_now=True)
name = models.CharField(max_length=30, blank=True)
class ExpectedNames(User):
class Meta:
proxy=True
if name == "Rick":
return User
else:
return User2
Admin
#admin.register(ExpectedNames)
class ExpectedNamesAdmin(admin.ModelAdmin):
date_hierarchy = 'created'
Any suggestions not sure if this is the correct way to do this.
I think this is not possible as it states in the Django Documentation:
Base class restrictions:
A proxy model must inherit from exactly one non-abstract model class. You can’t inherit from multiple non-abstract models as the proxy model doesn’t provide any connection between the rows in the different database tables. A proxy model can inherit from any number of abstract model classes, providing they do not define any model fields. A proxy model may also inherit from any number of proxy models that share a common non-abstract parent class.
https://docs.djangoproject.com/en/dev/topics/db/models/#proxy-models
I use magic method new in same situation.
I have model Documen with field document_type. If document_type is 'contract' i want ContractProxy, if 'offer' - OfferProxy.
For do this I create new proxy:
class RelatedDocumentProxy(Document):
class Meta:
proxy = True
def __new__(cls, *args, **kwargs):
doc_type = args[1]
if doc_type == 'contract':
return ContractProxy(*args, **kwargs)
return OfferProxy(*args, **kwargs)
document_type is first field and will first arg who pass to method

How to use two unique constraints in Django model?

I have a Django model for a player of a game
class Player(models.Model):
name = models.CharField(max_length=50)
team = models.ForeignKey('Team', on_delete=models.CASCADE, blank=True, null=True)
game = models.ForeignKey('Game', on_delete=models.CASCADE)
objects = GameManager()
class Meta:
unique_together = ('name', 'game',)
I have only one unique constraint, that the name and the game are unique together.
Now, I would like to extend our page by adding registered users. So, I would add this to the model.
user = models.ForeignKey('auth.User', on_delete=models.CASCADE, blank=True, null=True)
So, an registered user can subscribe to a game by adding a name, team, game, and his/her user. However, the user should only be able to add his account once to an game, which would be a second unique constrain
unique_together = ('user', 'game',)
Is it possible to give in Django two unique constraints to the model? Or do I have to search in the table manually prior to saving the new entries? Or is there a better way?
Yes, in fact by default unique_together is a collection of collections of fields that are unique together, so something like:
class Player(models.Model):
name = models.CharField(max_length=50)
team = models.ForeignKey('Team', on_delete=models.CASCADE, blank=True, null=True)
game = models.ForeignKey('Game', on_delete=models.CASCADE)
objects = GameManager()
class Meta:
unique_together = (('name', 'game',), ('user', 'game',))
Here we thus specify that every name, game pair is unique, and every user, game pair is unique. So it is impossible to create two Player objects for the same user and game, or for the same game and name.
It is only because a single unique_together constraint is quite common, that one can also pass a single collection of field names that should be unique together, as is written in the documentation on Options.unique_together [Django-doc]:
Sets of field names that, taken together, must be unique:
unique_together = (("driver", "restaurant"),)
This is a tuple of tuples 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).
For convenience, unique_together can be a single tuple when dealing with a single set of fields:
unique_together = ("driver", "restaurant")
You should use models.UniqueConstraint (reference).
As noted in the reference:
UniqueConstraint provides more functionality than unique_together. unique_together may be deprecated in the future.
Do this:
class Meta:
constraints = [
models.UniqueConstraint(fields=['name', 'game'], name="unique_name_game"),
models.UniqueConstraint(fields=['user', 'game'], name="unique_user_game"),
]
For example please refer to this :-
class Stores(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=50)
lat = models.FloatField()
lng = models.FloatField()
merchant = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name="stores")
def __str__(self):
return "{}: {}".format(self.name, self.address)
class Meta:
verbose_name_plural = 'Stores'
class Items(models.Model):
name = models.CharField(max_length=50, unique=False)
price = models.IntegerField()
description = models.TextField()
stores = models.ForeignKey(Stores, on_delete=models.CASCADE, related_name="items")
def __str__(self):
return self.name
class Meta:
verbose_name_plural = "Items"
unique_together = ('name', 'stores',)

Django Nested Form - Always Showing Object instead of model details

I'm working on a Django project generated via Mezzanine. I've been able to create my models, however I want to have a form where an admin can select from a list to assign a value in a many to many or a one to many relationship. For example, I have a model for Schemas:
class Schema(AutoCreatedUpdatedMixin, SoftDeleteMixin):
"""List of all Schemas in a given database"""
name = models.CharField(max_length=128, null=False)
status = models.BooleanField(max_length=128, null=False, default=True, verbose_name="Is Active")
description = models.CharField(max_length=65535, null=True, blank=True, default=None)
database = models.ForeignKey(Database, on_delete=models.CASCADE)
pull_requests = models.ManyToManyField(Link)
questions = models.ManyToManyField(Question, blank=True)
comments = models.ManyToManyField(Comment, blank=True)
technical_owners = models.ManyToManyField(Employee, related_name='technical_owners_schemas', blank=True)
business_owners = models.ManyToManyField(Employee, related_name='business_owners_schemas', blank=True)
watchers = models.ManyToManyField(Employee, related_name='watchers_schemas', blank=True)
def __unicode__(self):
return "{}".format(self.name)
And I have a model for Employees
class Employee(AutoCreatedUpdatedMixin, SoftDeleteMixin):
"""List of people with any involvement in tables or fields: business or technical owners, developers, etc"""
name = models.CharField(max_length=256, blank=False, null=False, default=None, unique=True)
email = models.EmailField(blank=True, null=True, unique=True)
def __unicode__(self):
return "{}".format(self.employee)
An employee can own multiple schemas and a schema can be owned by multiple employees. My database has an active employee in it, however when I try to create a Schema the employee shows up as Employee Object. Rather I would want the form to show the Employee.name. How can I do this? My admin file contains the following:
class SchemasAdmin(admin.ModelAdmin):
list_display = ['name', 'status', 'database', 'description']
ordering = ['status', 'database', 'name']
actions = []
exclude = ('created_at', 'updated_at', 'deleted_at')
First of all are you using python 2 or 3? For 3, the __str__ method should be used instead of __unicode__. I am writing this because it seems that there's a problem with the __unicode__ method of Employee, which although is defined as:
def __unicode__(self):
return "{}".format(self.employee)
th Employee class does not have an employee attribute (unless there's such an attribute in the mixins that class inherits from (AutoCreatedUpdatedMixin, SoftDeleteMixin) but I don't think that is the case.
In any case, the problem is that you haven't defined a propery __str__ (if using python 3) or __unicode__ (for python 2) method on the Employee class - just define it like:
return self.name
and you should see the employee's name in the django admin select fields.