Reference two foreign keys in Django data model - python

I want to reference two foreign keys in the Conversation model, because user_one and user_two can be a Person or Business either way. What is the best way of expressing this?
class Person(models.Model):
"""
Person model
"""
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
avatar = models.ImageField(upload_to=get_upload_avatar_path, blank=True, null=True, default=None, max_length=255)
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
class Business(models.Model):
"""
Business model
` """
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
name = models.CharField(max_length=255, null=True, default=None)
class Conversation(models.Model):
"""
Conversation model
Contains conversation relational data between registered users
"""
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
#user_one = models.ForeignKey(Person, null=True, default=None)
#user_two = models.ForeignKey(Business, null=True, default=None)
class ConversationReply(models.Model):
"""
Conversation reply model
Contains conversation reply data
"""
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
date_time = models.DateTimeField(blank=True, null=True, default=None)
conversation = models.ForeignKey(Conversation, null=True, default=None)
reply = models.CharField(max_length=255)

I would probably use django model inheratance and create an Entity model.
class Entity(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
class Meta:
abstract = True
class Person(Entity):
"""
Person model
"""
avatar = models.ImageField(upload_to=get_upload_avatar_path, blank=True, null=True, default=None, max_length=255)
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
class Business(Entity):
"""
Business model
` """
name = models.CharField(max_length=255, null=True, default=None)
class Conversation(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
content_object_1 = GenericForeignKey('content_type', 'object_1_id')
content_object_2 = GenericForeignKey('content_type', 'object_2_id')
Note that due to the use of GenericForeignKey filtering will not work the traditional way. Then you'll be able to do something like this:
from django.contrib.contenttypes.models import ContentType
contenttype_obj = ContentType.objects.get_for_model(person_object)
Conversation.objects.filter(object_id_1=person_object.id, content_type=contenttype_obj)

Related

Django: saving unique value without duplicate it

I'm trying to save unique name in the database but the problem I can save the same with different letters, for example I can save (IT, it, iT, It) I don't want to save it like that.
Model:
class Service(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(max_length=127, unique=True, null=False, blank=False) # that field
is_active = models.BooleanField(default=True)
is_deleted = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(
"accounts.User",
on_delete=SET_NULL,
blank=False,
null=True,
related_name="service_created_by",
)
def __str__(self):
return f"{self.name}"
A very simple solution:
class Service(models.Model):
name = models.CharField(max_length=50, unique=True)
....
def clean(self):
self.name = self.name.capitalize()
this one helped me
class Service(models.Model):
name = models.CharField(max_length=50, unique=True, null=False, blank=False)
....
class Meta:
constraints = [
models.UniqueConstraint(Lower("name"), name="unique_name"),
]
def clean(self):
self.name = self.name.capitalize()

How to create a view for a nested serializer with foreign keys in django rest framework?

I got this code, but I can't find a way to create a view that retrieve the allergies a patient has.
class Patient(models.Model):
user = models.OneToOneField(User, related_name='patient', on_delete=models.CASCADE)
id_type = models.CharField(max_length=300)
id_number = models.CharField(max_length=300)
creation_date = models.DateField(default=datetime.date.today)
class Allergie(models.Model):
name = models.CharField(max_length=300, default="X")
class PatientAllergies(models.Model):
patient = models.ForeignKey(Patient, related_name="patient_allergies", on_delete=models.CASCADE)
allergie = models.ForeignKey(Allergie, on_delete=models.CASCADE, null=True)
professional_contract = models.ForeignKey(ProfessionalContract, null=True ,on_delete=models.CASCADE)
You can span a ManyToManyField relation over your PatientAllergies model that acts as a junction table:
class Patient(models.Model):
user = models.OneToOneField(User, related_name='patient', on_delete=models.CASCADE)
id_type = models.CharField(max_length=300)
id_number = models.CharField(max_length=300)
creation_date = models.DateField(auto_now_add=True)
allergies = models.ManyToManyField(
'Allergie',
through='PatientAllergies'
)
# …
You can then for a Patient object p with:
p.allergies.all()
An alternative is to filter the Allergie objects with:
Allergie.objects.filter(patientallergies__patient=p)
or with the ManyToManyField:
Allergie.objects.filter(patient=p)

Add custom filename django model (models.FileField)

I have a model for my user and another model for music style. In my model of musical styles, I need to put the image under a name other than the original name.
The new name I would like is the 'email of the user who is saving' + '_' + 'milliseconds'.
Staying in this format:
'test#gmail.com_1621196336'
Model User:
class CustomUser(AbstractUser):
username = None
id = models.UUIDField(primary_key=True, unique=True, default=uuid.uuid4, editable=False, null=False)
email = models.EmailField('E-mail', unique=True, null=False)
Model Music Styles:
class MusicStyle(models.Model):
id = models.UUIDField(primary_key=True, unique=True, default=uuid.uuid4, editable=False, null=False)
name = models.CharField(max_length=150, null=False, blank=False)
image_link = models.FileField(upload_to='musics/thumbnail_images/##CUSTOM_NAME##')
Musical style is in a different model from the user model.
How to do this?
i didnt understand what the field with milliseconde is or what is supposed to do so i replaced it with with email and music_style name, here is how you can name your images, you can adjust it as you want
def upload_location(instance, filename):
filebase, extension = filename.split('.')
return 'musics/thumbnail_images/%s_%s.%s' % (instance.user.email,instance.name, extension)
class MusicStyle(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
name = models.CharField(max_length=150, null=False, blank=False)
image_link = models.FileField(upload_to=upload_location)
If you want to set custom filename for your files you can do this:
def image_upload_to(instance, filename):
# You can access to your model fields with instance for example: instance.name
return f"musics/thumbnail_images/{filename}"
class MusicStyle(models.Model):
id = models.UUIDField(primary_key=True, unique=True, default=uuid.uuid4, editable=False, null=False)
name = models.CharField(max_length=150, null=False, blank=False)
image_link = models.FileField(upload_to=image_upload_to)

Django - conditional foreign key

I have the following 4 models in my program - User, Brand, Agency and Creator.
User is a superset of Brand, Agency and Creator.
A user can be a brand, agency or creator. They cannot take on more than one role. Their role is defined by the accountType property. If they are unset (i.e. 0) then no formal connection exists.
How do I express this in my model?
User model
class User(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
email = models.EmailField(max_length=255, null=True, default=None)
password = models.CharField(max_length=255, null=True, default=None)
ACCOUNT_CHOICE_UNSET = 0
ACCOUNT_CHOICE_BRAND = 1
ACCOUNT_CHOICE_CREATOR = 2
ACCOUNT_CHOICE_AGENCY = 3
ACCOUNT_CHOICES = (
(ACCOUNT_CHOICE_UNSET, 'Unset'),
(ACCOUNT_CHOICE_BRAND, 'Brand'),
(ACCOUNT_CHOICE_CREATOR, 'Creator'),
(ACCOUNT_CHOICE_AGENCY, 'Agency'),
)
account_id = models.ForeignKey(Brand)
account_type = models.IntegerField(choices=ACCOUNT_CHOICES, default=ACCOUNT_CHOICE_UNSET)
class Meta:
verbose_name_plural = "Users"
def __str__(self):
return "%s" % self.email
Brand model
class Brand(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
name = models.CharField(max_length=255, null=True, default=None)
brand = models.CharField(max_length=255, null=True, default=None)
email = models.EmailField(max_length=255, null=True, default=None)
phone = models.CharField(max_length=255, null=True, default=None)
website = models.CharField(max_length=255, null=True, default=None)
class Meta:
verbose_name_plural = "Brands"
def __str__(self):
return "%s" % self.brand
Creator model
class Creator(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
first_name = models.CharField(max_length=255, null=True, default=None)
last_name = models.CharField(max_length=255, null=True, default=None)
email = models.EmailField(max_length=255, null=True, default=None)
youtube_channel_username = models.CharField(max_length=255, null=True, default=None)
youtube_channel_url = models.CharField(max_length=255, null=True, default=None)
youtube_channel_title = models.CharField(max_length=255, null=True, default=None)
youtube_channel_description = models.CharField(max_length=255, null=True, default=None)
photo = models.CharField(max_length=255, null=True, default=None)
youtube_channel_start_date = models.CharField(max_length=255, null=True, default=None)
keywords = models.CharField(max_length=255, null=True, default=None)
no_of_subscribers = models.IntegerField(default=0)
no_of_videos = models.IntegerField(default=0)
no_of_views = models.IntegerField(default=0)
no_of_likes = models.IntegerField(default=0)
no_of_dislikes = models.IntegerField(default=0)
location = models.CharField(max_length=255, null=True, default=None)
avg_views = models.IntegerField(default=0)
GENDER_CHOICE_UNSET = 0
GENDER_CHOICE_MALE = 1
GENDER_CHOICE_FEMALE = 2
GENDER_CHOICES = (
(GENDER_CHOICE_UNSET, 'Unset'),
(GENDER_CHOICE_MALE, 'Male'),
(GENDER_CHOICE_FEMALE, 'Female'),
)
gender = models.IntegerField(choices=GENDER_CHOICES, default=GENDER_CHOICE_UNSET)
class Meta:
verbose_name_plural = "Creators"
def __str__(self):
return "%s %s" % (self.first_name,self.last_name)
Agency model
class Agency(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
name = models.CharField(max_length=255, null=True, default=None)
agency = models.CharField(max_length=255, null=True, default=None)
email = models.EmailField(max_length=255, null=True, default=None)
phone = models.CharField(max_length=255, null=True, default=None)
website = models.CharField(max_length=255, null=True, default=None)
class Meta:
verbose_name_plural = "Agencies"
def __str__(self):
return "%s" % self.agency
Update:
So I've whittled it down to this bit here in the model:
ACCOUNT_CHOICE_UNSET = 0
ACCOUNT_CHOICE_BRAND = 1
ACCOUNT_CHOICE_CREATOR = 2
ACCOUNT_CHOICE_AGENCY = 3
ACCOUNT_CHOICES = (
(ACCOUNT_CHOICE_UNSET, 'Unset'),
(ACCOUNT_CHOICE_BRAND, 'Brand'),
(ACCOUNT_CHOICE_CREATOR, 'Creator'),
(ACCOUNT_CHOICE_AGENCY, 'Agency'),
)
account_type = models.IntegerField(choices=ACCOUNT_CHOICES, default=ACCOUNT_CHOICE_UNSET)
limit = models.Q(app_label='api', model='Brand') | \
models.Q(app_label='api', model='Creator') | \
models.Q(app_label='api', model='Agency')
content_type = models.ForeignKey(ContentType, limit_choices_to=get_content_type_choices(), related_name='user_content_type')
content_object = GenericForeignKey('content_type', 'email')
If account_type = 1 then link to brand model
If account_type = 2 then link to creator model
If account_type = 3 then link to agency model
How do I accomplish this? Getting this error:
File "/Users/projects/adsoma-api/api/models.py", line 7, in <module>
class User(models.Model):
File "/Users/projects/adsoma-api/api/models.py", line 28, in User
content_type = models.ForeignKey(ContentType, limit_choices_to=get_content_type_choices(), related_name='user_content_type')
NameError: name 'get_content_type_choices' is not defined
Have you tried exploring Django's GenericForeignKey field?
class User(models.Model):
...
limit = models.Q(app_label='your_app_label', model='brand') |
models.Q(app_label='your_app_label', model='creator') |
models.Q(app_label='your_app_label', model='agency')
content_type = models.ForeignKey(ContentType, limit_choices_to=limit, related_name='user_content_type')
object_id = models.UUIDField()
content_object = GenericForeignKey('content_type', 'object_id')
You can access the User's brand/creator/agency by using the following notation:
User.objects.get(pk=1).content_object
This will be an instance of Brand/Creator/Agency as per the content_type.
https://docs.djangoproject.com/en/stable/ref/contrib/contenttypes/#django.contrib.contenttypes.fields.GenericForeignKey
Update based on your comment
Re 1: Using email:
class User(models.Model):
...
email = models.EmailField(max_length=255, unique=True)
limit = models.Q(app_label='your_app_label', model='brand') |
models.Q(app_label='your_app_label', model='creator') |
models.Q(app_label='your_app_label', model='agency')
content_type = models.ForeignKey(ContentType, limit_choices_to=get_content_type_choices, related_name='user_content_type')
content_object = GenericForeignKey('content_type', 'email')
Note: Email can not be a nullable field anywhere if you follow this approach! Also this approach seems hacky/wrong since the email field is now declared in multiple places; and the value can change if you re-assign the content objects. It is much cleaner to link the GenericForeignKey using the discrete UUIDField on each of the other three models
Re 2: Using account_type field:
ContentType is expected to be a reference to a Django Model; therefore it requires choices that are Models and not integers.
The function of limit_choices_to is to perform a filtering such that all possible models are not surfaced as potential GenericForeignKey
class ContentType(models.Model):
app_label = models.CharField(max_length=100)
model = models.CharField(_('python model class name'), max_length=100)
objects = ContentTypeManager()
However, limit_choices_to does accept callables; so you can write a helper method that translates your account_type to the correct Model
I am not clear about how this transaltion should work; so I leave that to you.
Here is how I solve it, override the save method check your condition
tested on Django 3.2
CUSTOMER = [
('customer', 'customer'),
('supplier', 'supplier'),
]
class Customer(models.Model):
name = models.CharField(max_length=256, null=False, blank=False)
user_type = models.CharField(max_length=32, choices=CUSTOMER)
class SupplierOrder(models.Model):
price = models.FloatField(default=0)
supplier = models.ForeignKey(Customer, related_name='supplier', on_delete=models.CASCADE)
def save(self, *args, **kwargs):
supplier = get_object_or_404(Customer, id=self.supplier.id)
if supplier.user_type != 'supplier':
raise ValueError('selected user must be supplier')
super().save(*args, **kwargs)

Access child model attributes in django templates

I have created model inheritance in django models like:
models.py
class Instance(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(User, related_name='instances')
name = models.CharField(max_length=255, name='name')
slug = AutoSlugField(populate_from='name', unique=True)
created_at = models.DateTimeField(default=timezone.now, editable=False)
type = models.CharField(max_length=255, name='type', default='')
class Meta:
ordering = ['-created_at']
class CustomInstance(Instance):
serverCode = models.TextField(default='', blank=False)
jsonPackageCode = models.TextField(default='', blank=False)
type = models.CharField(max_length=255, name='type', default='custom')
class AwdInstance(Instance):
archive = models.FileField(upload_to='archives', name='archive')
type = models.CharField(max_length=255, name='type', default='awd')
class AwodInstance(Instance):
archive = models.FileField(upload_to='archives', name='archive')
type = models.CharField(max_length=255, name='type', default='awod')
class GithubInstance(Instance):
archive = models.CharField(max_length=255)
type = models.CharField(max_length=255, name='type', default='github')
How can I get child model fields in django templates like user.instances.serverCode?
Help me, please!
Thanks in advance!

Categories