Two players can register for a tennis tournament which offers the doubles discipline (2 players compete against 2 other players).
But there are some constraints:
The same player may not register as player_01 for the same competition twice (same for player_02)
A player may not register as player_01 if already registered as player_02 (with different partner) for the same competition (and vice versa)
class Registration(models.Model):
class Meta:
abstract = True
competition = models.ForeignKey(Competition, on_delete=models.CASCADE)
class DoubleRegistration(Registration):
class Meta:
constraints = [
# 1. The same player may not register as player_01 for the same competition twice (same for player_02)
models.UniqueConstraint(
fields=['competition', 'player_01'],
name='double_registration_unique_pl01'),
models.UniqueConstraint(
fields=['competition', 'player_02'],
name='double_registration_unique_pl02'),
# 2. A player may not register as player_01 if already registered as player_02 (with different partner) for the same competition (and vice versa)
models.CheckConstraint(
check=~Q(player_01=F('player_02') , competition=F('competition')),
name='double_registration_pl01_already_registered'),
models.CheckConstraint(
check=~Q(player_02=F('player_01'), competition=F('competition')),
name='double_registration_pl02_already_registered'),
]
player_01 = models.ForeignKey(Player, on_delete=models.CASCADE, related_name = 'doubles_player_01')
player_02 = models.ForeignKey(Player, on_delete=models.CASCADE, related_name = 'doubles_player_02')
While the first constraint seems to work fine, the second constraint does not work.
I am still able to first register Adam as player_01 and Kain as player_02 (which is fine) but then also register Adam as player_02 and Kain as player_01.
Since the second constraint checks if Adam has already been registered as player_01 for the same competition this should fail, but doesn't.
CheckConstraint constraints are not enforced at the database level. The check argument probably can't evaluate True or False given your code. Try custom validation method in the DoubleRegistration model. If that's not an option use a database-level trigger or maybe a database-level unique constraint. Your code might not prevent the same player from registering twice. Will this work instead?
class Registration(models.Model):
class Meta:
abstract = True
competition = models.ForeignKey(Competition, on_delete=models.CASCADE)
class DoubleRegistration(Registration):
class Meta:
constraints = [
# 1. The same player may not register as player_01 for the same competition twice (same for player_02)
models.UniqueConstraint(
fields=['competition', 'player_01'],
name='double_registration_unique_pl01'),
models.UniqueConstraint(
fields=['competition', 'player_02'],
name='double_registration_unique_pl02'),
]
player_01 = models.ForeignKey(Player, on_delete=models.CASCADE, related_name = 'doubles_player_01')
player_02 = models.ForeignKey(Player, on_delete=models.CASCADE, related_name = 'doubles_player_02')
Related
Imagine there are three models named Movie, Actor, and Participation.
class Movie(models.Model):
identifier = models.CharField()
class Actor(models.Model):
name = models.CharField()
class Participation(models.Model):
movie_identifier = models.CharField()
actor = models.ForgeinKey(Actor, on_delete=models.CASCADE)
Let's assume that I can't use ForgeinKey for the movie in the Participation model.
how can I retrieve all the participation records of a movie with only one query?
Here is the solution if I had a foreign key for the movie in the participation table:
qs = Movie.objects.filter(identifier="an_identiier").prefetch_related("participations_set")
How can I do this without having a Movie foreign key in the Participation model?
Thanks!
One of the most important things when designing a database (hence when designing your models) is database normalization [Wikipedia].
You talk about Participation being related to multiple models like Movie, Series, Episode, etc. this means that Movie, Series, Episode all can be said to have something in common or they can be said to be a specialization of another entity let us say Participatable for the lack of a better word, or we can say Participatable is a generalization of Movie, Series, Episode, etc.
How do we model these? Well we will just have an extra model that our other models will have a OneToOneField with:
class Participatable(models.Model):
# Any common fields here
MOVIE = 'M'
SERIES = 'S'
TYPE_CHOICES = [
(MOVIE, 'Movie'),
(SERIES, 'Series'),
]
subject = models.CharField(max_length=1, choices=TYPE_CHOICES)
class Movie(models.Model):
# uncommon fields
participatable = models.OneToOneField(
Participatable,
on_delete=models.CASCADE,
related_name='movie',
)
class Series(models.Model):
# uncommon fields
participatable = models.OneToOneField(
Participatable,
on_delete=models.CASCADE,
related_name='series',
)
class Participation(models.Model):
participatable = models.ForgeinKey(Participatable, on_delete=models.CASCADE)
actor = models.ForgeinKey(Actor, on_delete=models.CASCADE)
Other than this solution which I find is the best for such modelling you can go with using the content-types framework which will essentially do what you do currently. That is it will use a field that stores the related id and also a foreign key that points to an entry in a table that will simply describe which table this id is for.
I have the following models:
from django.db import models
from foo.bar.models import Location
class AbstractShop(models.Model):
location = models.ForeignKey(Location, on_delete=models.CASCADE, related_name="%(class)s")
class Meta(self):
abstract = True
class Bakery(AbstractShop):
some_field = models.BooleanField("Some field", default=True)
class Meta:
verbose_name = "Bakery"
verbose_name_plural = "Bakeries"
class Supermarket(AbstractShop):
some_other_field = models.CharField("Some other field", max_length=20)
class Meta:
verbose_name = "Supermarket"
verbose_name_plural = "Supermarkets"
Now, Supermarket as well as Bakery inherit the location-ForeignKey from AbstractShop.
If I want to query the reverse relation to Bakery on the Location model, I would have to use bakerys (instead of the correct bakeries) as related_name - which I don't want as it's grammatically wrong and unintuitive.
So my questions are:
Is there any way to use the verbose_name_plural as related_name?
Is there any way at all to use any other related_name than "%(class)s" or "%(app_label)s or do I just have to implement the ForeignKey on the child classes?
If so, that would be somewhat annoying. Imagine you have a lot of shared ForeignKeys: You can move the ones with regular plurals in English to the abstract base class (ABC) (because for regular nouns the added "s" in "%(class)s" results in the correct plural form) while those with irregular plurals have to be implemented on the child class (as only there the related_name can be set to the plural of the actual name of the child class, which you don't know in the ABC).
This is a totally arbitrary condition which might not be obvious to non-native English speakers, also it transfers linguistic logic to code, which IMHO shouldn't happen.
I want to make Django Model fields unique with two fields(values) in some conditions.
there's two fields: 'team', 'type'. And I want to make team manager unique
For Example:
team=1, type='manager'
team=1, type='manager'
-> Not available
team=1, type='manager'
team=1, type='member'
team=1, type='member'
team=2, type='manager'
-> Available
I think unique_together('team', 'type') won't work properly with this situation.
How can I make this with Django Model?
Here's my model below:
class myModel(models.Model):
team = models.ForeignKey('Team', on_delete=models.CASCADE)
type = models.CharField(max_length=10, default='member')
class Meta:
db_table = 'my_models'
I think, You need to use UniqueConstraint for your application which work perfect in kind of situation.
class myModel(models.Model):
team = models.ForeignKey('Team', on_delete=models.CASCADE)
type = models.CharField(max_length=10, default='member')
class Meta:
db_table = 'my_models'
constraints = [
models.UniqueConstraint(fields=['team', 'type'], name='unique_team')
]
you can also refer this link for more understanding. and let me know if following solution will work.
Given that there is a deprecation warning in the documentation (based on 3.2 docs) for unique_together, I think it's worth showing that this can be done using UniqueConstraint. I believe that the key missing ingredient from the previous answer is the use of UniqueConstraint.condition, like so:
from django.db import models
from django.db.models import Q, UniqueConstraint
class myModel(models.Model):
team = models.ForeignKey('Team', on_delete=models.CASCADE)
type = models.CharField(max_length=10, default='member')
class Meta:
db_table = 'my_models'
constraints = [
UniqueConstraint(
fields=['team', 'type'],
name='unique_team',
condition=Q(type='manager')
)
]
Let's say I have 100 players and 10 teams how can I remove any player from FroreignKey drop-down chooser that is already was chosen for another team?
inside SpielerTeamRelationshipModel I have player = models.ForeignKey(Spieler) and I would like it not to show players who have already been selected for another teams. Is this possible?
class Spieler(models.Model):
name = models.CharField(max_length=128)
vorname = models.CharField(max_length=128)
panels = [
FieldPanel('name', classname="col6"),
FieldPanel('vorname', classname="col6"),
]
class SpielerTeamRelationshipModel(models.Model):
player = models.ForeignKey(Spieler)
in_team_seit = models.DateField()
page = ParentalKey('TeamRooster',
related_name='spieler_in_team')
panels = [
FieldPanel('player', classname="col6"),
FieldPanel('in_team_seit', classname="col6")
]
class TeamRooster(Page):
content_panels = [
FieldPanel('title'),
InlinePanel(
'spieler_in_team', label="Spieler",
panels=None)
]
One Player can be only in one Team. One Team can have one or more players. Very convenient for this is InlinePanel, but every time to choose one player from 100, it's excruciating.
Or maybe I'm not doing the right thing and there's a smarter way to solve this problem?
Strange but no one have mentioned unique_together. It will help you to create unique relations between team and player.
class Player(models.Model):
name = models.CharField(max_length=128)
team = models.ForeignKey(Team, blank=True, null=True)
class Meta:
...
unique_together = ("id", "team")
or sometimes you will need this
class Meta:
...
unique_together = ("name", "team")
Good luck!
Instead of having a player as FK in team
You can solve the problem by having team as FK in Player table
I am trying to implement a voting system that keeps track of votes for each type of user on my site. My plan was to create a Vote model that keeps track of Up votes and Total votes for each user type and calculates the percentage of Up votes.
Hardcoded that looks something like this:
class Eduuser(AbstractUser):
TYPE_1 = 'TY1'
TYPE_2 = 'TY2'
...
USER_TYPE_CHOICES = (
(TYPE_1, 'Type 1'),
(TYPE_2, 'Type 2'),
...
)
user_type = models.CharField(max_length=3, choices=USER_TYPE_CHOICES)
class Vote(models.Model):
a = models.IntegerField(
default=0, name=getattr(Eduuser, 'TYPE_1')+'_up')
b = models.IntegerField(
default=0, name=getattr(Eduuser, 'TYPE_2')+'_up')
...
c = models.IntegerField(
default=0, name=getattr(Eduuser, 'TYPE_1')+'_votes')
d = models.IntegerField(
default=0, name=getattr(Eduuser, 'TYPE_2')+'_votes')
...
def perc(self):
perc_array = []
for user_type in getattr(Eduuser, 'USER_TYPE_CHOICES'):
up = float(getattr(self, user_type[0]+'_up')) #Prevent int division
votes = getattr(self, user_type[0]+'_votes')
if votes==0:
perc_array.append(0)
else:
perc_array.append(round(up/votes, 3))
return perc_array
Although I don't anticipate adding more types, I would like for the code to look cleaner. My best attempt at looping over the user types was:
class Eduuser(AbstractUser):
...
class Vote(models.Model):
for user_type in getattr(Eduuser, 'USER_TYPE_CHOICES'):
models.IntegerField(
default=0, name=user_type[0]+'_up')
models.IntegerField(
default=0, name=user_type[0]+'_votes')
def perc(self):
...
However this does not save the fields (I guess because of the lack of assignment operator).
So a couple of quick questions:
1) Is there a way to save fields without explicitly assigning them a name? Or can I convert the string name into a variable (from other posts I've read, this seems like a bad idea)?
2) Am I even approaching this voting idea logically? Part of me feels like there is a far easier approach to keeping track of votes for multiple types of users.
Any help is appreciated! Thanks!
django-model-utils can make this cleaner with it's Choices helper.
You could do a Vote model in the following way (untested):
from model_utils import Choices
class User(AbstractUser):
USER_CHOICES = Choices(
('one', 'Type 1'),
('two', 'Type 2'),
)
user_type = models.CharField(max_length=10, choices=USER_CHOICES)
class Vote(models.Model):
"""
A single vote on a `User`. Can be up or down.
"""
VOTE_CHOICES = Choices(
('upvote'),
('downvote'),
)
user = models.ForeignKey(User)
vote = models.CharField(max_length=10, choices=VOTE_CHOICES)
Example usage – get the number of positive votes for all “Type 1” Users:
# retrieve all of the votes
all_votes = Vote.objects.all()
all_votes_count = len(all_votes)
# now retrieve all of the votes for users of ‘Type 1’
type_one_votes = all_votes.filter(user__user_type=User.USER_CHOICES.one)
type_one_votes_count = len(type_one_votes)
# …and now filter the positive votes for ‘Type 1’ users
type_one_positives = type_one_votes.filter(vote=Vote.VOTE_CHOICES.upvote)
type_one_positive_vote_count = len(type_one_positives)
# etc.
Django uses some metaclass behavior to create fields based on what you declare, so this is not wholly trivial. There are some undocumented calls you can use to dynamically add fields to your model class - see this post:
http://blog.jupo.org/2011/11/10/django-model-field-injection/
That said, I would recommend a simpler approach. Create a model to hold your possible user types, then use it as a foreign key in the votes table:
class UserType(models.Model):
type_name = models.CharField()
class Vote(models.Model):
user_type = models.ForeignKey(UserType)
total = models.PositiveIntegerField()
Or track the individual votes and sum as needed, either recording the user who cast the vote or just the user's type at the time the vote was cast. Depending on what you want to do if a user changes classes after voting you might need to save the user's type anyway.
If you do just track the sums, you have to think more carefully about transaction issues - I'd say track the user and enforce a uniqueness constraint.