How to set Djangos through_field to an Intermediate model nested field - python

Is there any way I can set through_field to an intermediatory models's nested field,
Just for an example:
class Person(models.Model):
name = models.CharField(max_length=50)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(
Person,
through='Membership',
through_fields=('assignee__group', 'person'),
)
class GroupLeader(models.Model)
identity = models.ForeignKey(Person)
group = models.ForeignKey(Group)
#more fields
class Membership(models.Model):
assignee = models.ForeignKey(GroupLeader)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
# more fields
I tried doing it but I am getting an error:
The intermediary model 'Membership' has no field 'assignee__group'
NOTE: The above is just an example, just in case such a situation is encountered.

Your group leader should NOT be part of the M2M relation, this is just a metadata. So just add group into the Membership class.
class Person(models.Model):
name = models.CharField(max_length=50)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(
Person,
through='Membership',
)
class GroupLeaders(models.Model)
identity = models.ForeignKey(Person)
group = models.ForeignKey(Group)
class Membership(models.Model):
group = models.ForeignKey(Group, on_delete=models.CASCADE)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
assignee = models.ForeignKey(GroupLeaders)
or you can even completely strip out the GroupLeaders class
class Person(models.Model):
name = models.CharField(max_length=50)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(
Person,
through='Membership',
through_fields=('group', 'person'),
)
class Membership(models.Model):
group = models.ForeignKey(Group, on_delete=models.CASCADE)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
leader = models.ForeignKey(Person, on_delete=models.CASCADE,
related_name="leading_memberships",
)

Related

Django annotate field with queryset of objects

Suppose I have two models:
ModelA and ModelB
How can I annotate a queryset of ModelB with objects from ModelA?
queryset = ModelB.objects.filter(...).annotate(models_a=Subquery(ModelA.objects.filter(...)))
In order to have queryset.models_aas a Queryset of objects ModelA.
Thanks you all!
EDIT:
This are my models:
class Allergen(models.Model):
name = models.CharField(_('Allergen name'), max_length=20, choices=ALLERGENS,
help_text=_('Product allergen. Example: gluten'), unique=True)
def __str__(self):
return self.name
class Meta:
ordering = ['name']
class Product(models.Model):
name = models.CharField(_('Name'), max_length=255, help_text=_('Product name'))
supplier = models.ForeignKey(Supplier, blank=True, null=True, related_name='supplier_products',
on_delete=models.CASCADE)
allergens = models.ManyToManyField(Allergen, blank=True, related_name='product_allergens')
unit = models.CharField(_('Unit'), max_length=20, choices=UNITS, default='g')
price = models.FloatField(_('Sale price'), default=0)
unit_price = models.FloatField(_('Unit price'))
class Meta:
ordering = ['name', ]
indexes = [models.Index(fields=['name', 'supplier']), ]
class Recipe(models.Model):
sections = models.ManyToManyField(Section, related_name='recipes', blank=True)
title = models.CharField(_('Recipe title'), max_length=255, help_text=_('Recipe. Example: american chicken salad'),
blank=True)
unit = models.CharField(_('Unit'), max_length=20, choices=UNITS)
user = models.ForeignKey(User, null=True, on_delete=models.CASCADE, related_name='user_recipes')
class Meta:
ordering = ['title']
indexes = [models.Index(fields=['title'])]
class IngredientRecipe(models.Model):
name = models.CharField(_('Name'), max_length=255)
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE, related_name='ingredients')
products = models.ManyToManyField(Product, related_name='ingredient_products')
quantity = models.FloatField(_('Quantity'))
class Meta:
ordering = ['-id']
unique_together = ('name', 'recipe')
indexes = [models.Index(fields=['name', 'recipe'])]
I'm trying to include the allergens related with the recipe.
If you are working with just one recipe, you can just add a model property that returns the related allergens of the recipe by using the reverse relations like this:
class Recipe(models.Model):
...
#property # or consider using cached_property
def allergens(self):
return Allergen.objects.filter(
product_allergens__ingredient_products__recipe=self,
)
Then you can access the allergens through the instance:
recipe = Recipe.objects.first()
print(recipe.allergens)

ManyToMany Field Refere to Itself

How can make my model so that its ManyToMany Refer to User
class User(AbstractUser):
teacher_or_student = models.CharField(max_length=100)
mobile_number = models.CharField(max_length=100)
grade = models.CharField(max_length=100)
laptop_yes_or = models.CharField(max_length=100)
students = models.ManyToManyField(User)
You can pass the 'self' string for this. By default a ManyToManyField that refers to itself, is als symmetrical, so you probably want to turn that off, since if a is a student of b, then b is not per se a student of a. You can do that by specifying symmetrical=False [Django-doc]:
class User(AbstractUser):
teacher_or_student = models.CharField(max_length=100)
mobile_number = models.CharField(max_length=100)
grade = models.CharField(max_length=100)
laptop_yes_or = models.CharField(max_length=100)
students = models.ManyToManyField(
'self',
symmetrical=False,
related_name='teachers'
)

Multiple user types and ratings in Django

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)

How can I access model fields from a foreign key which is link to another model?

I have a model which contains a user, each user is associated to a club, a club can contain many teams, pitches etc. I want to know how I should design my models so that I can display/edit information on teams/pitches based on the user logged in and the club associated to that user. My ClubInfo model contains a foreign key associated to the user, where as my other models (Team/Pitch) have foreign keys associated to the ClubInfo not the user.
class ClubInfo(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
club_name = models.CharField(max_length=50, default='')
club_logo = models.ImageField(upload_to='profile_pics', blank=True)
club_address1 = models.CharField(max_length=30)
club_address2 = models.CharField(max_length=30, default='')
club_address3 = models.CharField(max_length=30, default='')
club_town = models.CharField(max_length=30)
club_county = models.CharField(max_length=30)
club_country = models.CharField(max_length=30)
def __str__(self):
return self.club_name
class Player(models.Model):
club_name = models.ForeignKey(ClubInfo, to_field='id', on_delete=models.CASCADE, unique=True)
first_name = models.CharField(max_length=20)
last_name = models.CharField(max_length=20)
dob = models.DateField(max_length=8)
email = models.EmailField(max_length=50)
phone = models.CharField(max_length=12)
mobile = models.CharField(max_length=15)
emergency_contact_name = models.CharField(max_length=40)
emergency_contact_mobile = models.CharField(max_length=15)
address1 = models.CharField(max_length=30)
address2 = models.CharField(max_length=30, default='')
address3 = models.CharField(max_length=30, default='')
town = models.CharField(max_length=30)
county = models.CharField(max_length=30)
country = models.CharField(max_length=30)
def __str__(self):
return "%s %s" % (self.first_name, self.last_name)
class Team(models.Model):
club_name = models.ForeignKey(ClubInfo, to_field='id', on_delete=models.CASCADE, unique=True)
team_name = models.CharField(max_length=30)
manager_name = models.CharField(max_length=20)
player_pk = models.ForeignKey(Player, to_field='id', on_delete=models.CASCADE, unique=True)
def __str__(self):
return self.team_name
class Pitch(models.Model):
club_name = models.ForeignKey(ClubInfo, to_field='id', on_delete=models.CASCADE, unique=True)
pitch_name = models.CharField(max_length=30)
PITCH_SIZES = (
('S', 'Small'),
('M', 'Medium'),
('L', 'Large'),
)
PITCH_TYPE = (
('1', 'Outdoor'),
('2', 'Indoor'),
)
pitch_size = models.CharField(max_length=1, choices=PITCH_SIZES)
pitch_type = models.CharField(max_length=1, choices=PITCH_TYPE)
open_time = models.TimeField(default='09:00')
close_time = models.TimeField(default='22:00')
def __str__(self):
return self.pitch_name
club can contain many teams, pitches etc.
Then you probably don't want to use unique=True in your foreign keys,
since the behaviour is quite similar to OneToOneField. That would mean that
each Club can only have one player, one pitch etc.
Please refer to:
What's the difference between django OneToOneField and ForeignKey?
You don't need to write explicitly to_field='id', it's the default value for a to_field argument
https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.to_field
so that I can display/edit information on teams/pitches based on the user logged
You need to define reverse relation:
https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.related_name
For example:
class Pitch(models.Model):
club_name = models.ForeignKey(ClubInfo, on_delete=models.CASCADE, related_name="pitches")
(...)
Then you can query for player's pitches in the following way:
player = Player.objects.get(<get your player>).club_name.pitches
In your current design (after removing a unique=True) a team can consist
of only one player, and players can be in many teams. Is that expected?
Most likely you would like to have
1) Team with many players - then you would need to add foreign key to Team in a Player model
2) Team with many players, but also players can be in many teams - then you would like to have a many-to-many relationship:
https://docs.djangoproject.com/en/dev/topics/db/examples/many_to_many/
club_name as a foreign key is a bad naming convention - it does not point to club_name, but to Club object
club = ClubInfo.objects.filter()
blog = Player.objects.filter(Q(club_name__in=club))

How to sort django list filter by alphanumeric

I have used default django filter but it not show record by sorting i.e alphanumeric
following is my code
class VoteAdminModel(admin.ModelAdmin):
list_filter = ('business__poll__town__parent')
models
class Business(models.Model):
poll = models.ForeignKey('Poll')
business_name = models.CharField(max_length=256, blank=True,null=True)
class Poll(models.Model):
town = models.ForeignKey('Town', verbose_name="Survey")
category = models.ForeignKey(Category)
class Vote(models.Model):
business = models.ForeignKey(Business)
vote_year = models.IntegerField(verbose_name="Year")
gender = models.CharField(max_length=10)
class Town(models.Model):
town_name = models.CharField(max_length=256, verbose_name="Survey")
parent = models.ForeignKey('self', db_column='parent', blank=True, null=True, verbose_name="Survey")

Categories

Resources