I am struggling with Many-to-Many model and form. What is the best way to show extra level field when creating a new Person with skill. I am going also to edit it later.
I am trying to achieve an effect in forms like that:
[input text] name
[select] skill name (there are choices in the model)
[input text] level of the skill
I tried things which people were suggesting but I couldn't make it to work. For example inlineformset + through.
Later on, I would like also to give the user a chance to add multiple sets of skill+level, maybe it is worth to think about it in advance?
My models and form:
class PersonSkill(models.Model):
person = models.ForeignKey('Person', on_delete=models.CASCADE)
skill = models.ForeignKey('Skill', on_delete=models.CASCADE)
level = models.CharField(max_length=50, choices=[(1, 'working'),
(2, 'advanced'),
(3, 'champion')], null=True, blank=True)
class Skill(models.Model):
name = models.CharField(max_length=50, null=True, blank=True)
def __str__(self):
return self.name
class Person(models.Model):
name = models.CharField(max_length=30, null=True, blank=True)
skill = models.ManyToManyField('Skill', through='PersonSkill', blank=True)
def __str__(self):
return self.name
class PersonForm(forms.ModelForm):
class Meta:
model = Person
fields = ('name',)
Related
I'm trying to create a workout tracking application where a user can:
Create an instance of an ExerciseTemplate model from a list of available Exercise models. I've created these as models so that the user can create custom Exercises in the future. There is also an ExerciseInstance which is to be used to track and modify the ExerciseTemplate created by the user, or someone else. I'm stripping the models of several unimportant fields for simplicity, but each contains the following:
class Exercise(models.Model):
# Basic Variables
name = models.CharField(max_length=128)
description = models.TextField(null=True, blank=True)
def __str__(self):
return self.name
class ExerciseTemplate(models.Model):
# Foreign Models
workout = models.ForeignKey(
'Workout',
on_delete=models.SET_NULL,
null=True,
blank=True
)
exercise = models.ForeignKey(
Exercise,
on_delete=models.SET_NULL,
null=True,
blank=True
)
recommended_sets = models.PositiveIntegerField(null=True, blank=True)
class ExerciseInstance(models.Model):
""" Foreign Models """
exercise_template = models.ForeignKey(
ExerciseTemplate,
on_delete=models.SET_NULL,
null=True,
blank=True
)
workout = models.ForeignKey(
'Workout',
on_delete=models.SET_NULL,
null=True,
blank=True
)
""" Fields """
weight = models.PositiveIntegerField(null=True, blank=True)
reps = models.PositiveIntegerField(null=True, blank=True)
Create a WorkoutInstance from a WorkoutTemplate. The WorkoutTemplate is made up of ExerciseTemplates. But the WorkoutInstance should be able to take the WorkoutTemplate and populate it with ExerciseInstances based on the ExerciseTemplates in the WorkoutTemplate. Here are the models that I have so far:
class WorkoutTemplate(models.Model):
name = models.CharField(max_length=128)
description = models.TextField(null=True, blank=True)
date = models.DateTimeField(null=True, blank=True)
#category...
exercises = models.ManyToManyField(
Exercise,
through=ExerciseTemplate
)
def __str__(self):
return self.name
class WorkoutInstance(models.Model):
# Foreign Models
workout_template = models.ForeignKey(
'WorkoutTemplate',
on_delete=models.SET_NULL,
null=True,
blank=True
)
But this is where I get stuck. I'm not sure how to proceed. My intuition is one of the following:
I need to create a more simple architecture to do this. I'll take any suggestions.
I need to create a method within the model that solves this issue. If this is the case, I'm not sure what this would actually look like.
When you create a new WorkoutInstance object which references a given WorkoutTemplate object you get all its related ExerciseTemplate objects.
Then you just create a new object (row) for each ExerciseInstance in another model (table)
If you link your ExerciseInstance to WorkoutInstance via 'workout' you could do something like:
wt = WorkoutTemplate.get(id=1)
wi = WorkoutInstance.create(workout_template=wt)
for e in wt.exercisetemplate_set.all:
ExerciseInstance.create(exercise_template=e, workout=wi)
You can implent this in the method that creates the new WorkoutInstance or take a look at signals
https://docs.djangoproject.com/en/4.0/topics/db/optimization/#create-in-bulk
I'm making a DRF backend with three user types: customer, personal trainer and gym owner. I want all the fields in the CustomUser class to apply to each user type. I also want some specific attributes to each user type (for example photo only for personal trainer and gym owner). Is this the right way to do it?
# models.py
class CustomUser(AbstractUser):
USER_TYPE_CHOICES = (
('customer'),
('personal_trainer'),
('gym_owner'),
)
user_type = models.CharField(blank=False, choices=USER_TYPE_CHOICES)
name = models.CharField(blank=False, max_length=255)
country = models.CharField(blank=False, max_length=255)
city = models.CharField(blank=False, max_length=255)
phone = models.CharField(blank=True, max_length=255)
ratings = models.ForeignKey(Rating, on_delete=models.CASCADE, null=True)
created_at = models.DateField(auto_now_add=True)
def __str__(self):
return self.name
class Customer(models.Model):
user = models.OneToOneField(
CustomUser, on_delete=models.CASCADE, primary_key=True)
class PersonalTrainer(models.Model):
user = models.OneToOneField(
CustomUser, on_delete=models.CASCADE, primary_key=True)
photo = models.ImageField(upload_to='photos/%Y/%m/%d/', blank=True)
class GymOwner(models.Model):
user = models.OneToOneField(
CustomUser, on_delete=models.CASCADE, primary_key=True)
photo = models.ImageField(upload_to='photos/%Y/%m/%d/', blank=True)
I also have a ratings model. I want to be able to leave a rating as a customer to a personal trainer or a gym. Each rating will have a one to one relation with it's owner and it's target. I'm not sure however how I can make the relations..?
# models.py
class Rating(models.Model):
STAR_CONVERSION = (
(1, 'One Star'),
(2, 'Two Stars'),
(3, 'Three Stars'),
(4, 'Four Stars'),
(5, 'Five Stars'),
)
rating = models.PositiveSmallIntegerField(choices=STAR_CONVERSION)
caption = models.TextField(blank=True)
owner = models.OneToOneField(Customer, on_delete=models.CASCADE)
# I want a target as a one to one relation to either PersonalTrainer or GymOwner
target = models.OneToOneField(*either personal trainer or gym owner*)
You need to make both owner and target a ForeignKey rather than a OneToOneField. With the latter, you could only have one rating for every customer and one for every provider, which would be a bit restrictive :).
For PersonalTrainer and GymOwner, you need model inheritance. The parent model would either be an abstract class (with the data saved in the tables of the individual child models), or (preferably in this case as the fields of both models are the same) the data would be saved in the parent model (e.g. Provider), while the child models would be proxy models based on the parent model's data, but providing different behaviour where appropriate.
The Django docs have quite a lot to say about the different inheritance options.
class Provider(models.Model):
user = models.OneToOneField(
CustomUser, on_delete=models.CASCADE, primary_key=True)
photo = models.ImageField(upload_to='photos/%Y/%m/%d/', blank=True)
class PersonalTrainer(Provider):
class Meta:
proxy = True
class GymOwner(Provider):
class Meta:
proxy = True
class Rating(models.Model):
# ...
owner = models.ForeignKey(Customer, on_delete=models.CASCADE)
target = models.ForeignKey(Provider, on_delete=models.CASCADE)
The use case of my application is I will have various fields to fill and among them one is Industry field and another is Segment Field for brand. The industry field is like category that brand falls into. So, if i choose the industry as Health Care for XYZ brand then the segment field should show the items like 'Ayurveda', 'Dental Clinics' (all health care related items). Basically, its like sub-category.
Here is a sample model
class Industry(models.Model):
name = models.CharField(max_length=150, blank=True, null=True)
class Meta:
verbose_name = 'Industry'
verbose_name_plural = 'Industries'
def __str__(self):
return self.name
class Segment(models.Model):
industry = models.ForeignKey(Industry, related_name='segment', on_delete=models.CASCADE)
name = models.CharField(max_length=150, blank=True, null=True)
class Meta:
verbose_name = 'Segment'
verbose_name_plural = 'Segments'
def __str__(self):
return f'{self.industry.name} - {self.name}'
class BusinessModel(models):
industry = models.ForeignKey(Industry, blank=False, null=False, related_name='industry', on_delete=models.CASCADE)
# segements = models.ForeignKey()
total_investment = models.CharField() # will be choice field
This is a simple model and I have not created Segment model as I am not sure how to approach to this problem. I am just curios to know, if for such case, do i have to something special in models.py or in the view side. Such type of things get arise during development phase, thus, I want to be clear on problem solving pattern in django.
UPDATE
https://www.franchisebazar.com/franchisor-registration here if you choose industry inside Business model section, the segment will be updated accordingly.
You can have a 3 model design like
class Industry(models.Model):
name = models.CharField(max_length=150, blank=True, null=True)
class Segment(models.Model):
name = models.CharField(max_length=150, blank=True, null=True)
class Mapping(models.Model):
industry = models.ForeignKey(Industry)
segment = models.ForeignKey(Segment)
You need to define relations between your models. You can find documentation about ManyToMany relation here which is suitable in your case.
you can use ChainedForeginKey.. Check the below links
customizing admin of django to have dependent select fields
https://django-smart-selects.readthedocs.io/en/latest/installation.html
Take the following as an example:
class Manufacturer(models.Model):
name = models.CharField(max_length=200, blank=False)
slug = models.SlugField(max_length=200, blank=False, unique=True)
class Category(models.Model):
name = models.CharField(max_length=200, blank=False)
slug = models.SlugField(max_length=200, blank=False, unique=True)
class Product(models.Model):
category = models.ForeignKey(Category, related_name='products')
manufacturer = models.ForeignKey(Manufacturer, related_name='products')
name = models.CharField(max_length=50, blank=False)
slug = models.SlugField(max_length=200, blank=False, unique=True)
class CarMixin(models.Model):
color = models.CharField(max_length=50, blank=False)
num_wheels = models.IntegerField()
class CellPhoneMixin(models.Model):
screen_size = models.IntegerField()
class Car(Product, CarMixin):
pass
class Food(Product):
pass
class CellPhone(Product, CellPhoneMixin):
pass
what I am trying to do is create a 'base' class called Product and apply a variety of 'sub-product-types' using Model Inheritance.
The reason I am not using the Django's 'Abstract Model Inheritance' is because I actually want a table in my DB to hold all the products, so I can make a query like Product.objects.all() to get all the products, then type product.car to get all the field values for the related Car object.
However, the problem I am running into is that a product can have a relationship with any one of Car, Food or CellPhone. So, I can't do product.car if it has a relationship with Food. There is no way in knowing which 'relationship' a Product object has. Is it related to Car? Food? CellPhone?
How should I approach this? Is there a better database schema I should be using?
I'm confused with my django models,
My models:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
picture = models.ImageField(upload_to="photos/", default="photos/none/default.png")
film = models.ManyToManyField(Film)
class Film(models.Model):
title = models.CharField(max_length=60)
year = models.IntegerField(choices=YEARS)
image = models.ImageField(upload_to="images/", default="images/none/blank_poster.jpg")
def __str__(self):
return self.title
and now I trying to make a ratings for my film, when user adding a film to his list.
I tried M2M with through, but it wasn't exactly what I wanted, because user could add the same film several times and another problem with it was remove single film from list.
Now i thinking about additional models like this:
class FilmRating(models.Model):
user = models.ForeignKey(User)
film = models.ForeignKey(Film)
rate = models.IntegerField(choices=CHOICES)
Im glad If you can point me to the correct way to solve this problem, In future I want probably to store all rating from users for set average rate.