Save() in ManyToMany relation - python

I have two models with ManyToMany relation. Here is the code:
class Player(models.Model):
first_name = models.CharField(max_length = 30, verbose_name = u"First name")
last_name = models.CharField(max_length = 50, verbose_name = u"Last name")
def __unicode__(self):
return "%s %s" % (self.last_name, self.first_name)
class Tournament(models.Model):
title = models.CharField(max_length = 100, verbose_name = u"Tournament's title")
players = models.ManyToManyField(Player,verbose_name = u"Tournament's players")
def __unicode__(self):
return self.title
def save(self, **kwargs):
Tournament(title = self.title)
all_players = Player.objects.all()
for member in all_players:
member_of_tournament = member.tournament_set.filter(title = self.title)
for j in member_of_tournament:
print member.tournament_set.filter(title = self.title)
self.players.add(member)
super(Tournament, self).save(**kwargs)
When I save Tournament for the first time it saves only the title. But when I save next time it saves players too and relates them to the Tournament. How can I save them at the same time with the tournament?

I think you have a number issues here:
class Tournament(models.Model):
title = models.CharField(max_length = 100, verbose_name = u"Tournament's title")
players = models.ManyToManyField(Player,verbose_name = u"Tournament's players")
def __unicode__(self):
return self.title
def save(self, **kwargs):
Tournament(title = self.title)
all_players = Player.objects.all()
for member in all_players:
member_of_tournament = member.tournament_set.filter(title = self.title)
for j in member_of_tournament:
print member.tournament_set.filter(title = self.title)
self.players.add(member)
super(Tournament, self).save(**kwargs)
In general, you don't want to have your m2m relationships in the model save method (and in this case, the logic for it isn't very good in any case)
You have a few issues in the save method itself, so let me address those:
def save(self, **kwargs):
Tournament(title = self.title)
The last line above is doing nothing. You instantiate an instance of Tournament, but don't save it to a variable. You actually already have an instance of tournament anyway (called self, if this case).
all_players = Player.objects.all()
for member in all_players:
member_of_tournament = member.tournament_set.filter(title = self.title)
Here, you loop through all the players in the DB, whether they match your query or not.
This is really inefficient.
In the next line, you have member_of_tournament = member.tournament_set.filter(title = self.title). This is plural, so you should call this members_of_tournament because this is an array / list / queryset.
I'm honestly not sure what the strategy is of the rest, but suffice to say, you shouldn't do it like that, probably.
You should just get ride of the entire custom save method and in your view that is driving this, you should do something like this:
tournament = Tournament(title=title)
tournament.save()
players_i_care_about = [players, go, here]
tournament.players = players_i_care_about #(removes all players and saves new players)
or
for player in players_i_care_about:
tournament.players.add(player) #(only adds these players, doesn't remove any players)
The reason for this, your view knows about what players belong to what tournament, but your model should be agnostic to that logic.

Related

How to delete a model instance and modify the user profile when another model instance is created?

I'm devoleping a website where you can see different matches and vote for the team you think that will win.
When the match ends, I would like to create an instance in the django admin with the match and the winner of the match, delete all the votes and add points to every user that voted for the winning team.
These are my models:
class Prognostic(models.Model):
match = models.IntegerField()
user = models.IntegerField()
vote = models.CharField(max_length = 100)
def __str__(self):
return self.vote
class MatchResult(models.Model):
match = models.ForeignKey("MatchOpponents")
winner = models.ForeignKey("TeamGGC")
#receiver(post_save, sender=Prognostic)
def voteRepartitionPoints(sender, instance, **kwargs):
profil = apps.get_model("userMembers", "Profil")
for prognostic in Prognostic().object.filter(match = match_id):
if prognostic.vote == winner:
user = profil.object.get(user_id = prog.user)
user.nbGGCPoints += 50
user.save()
prognostic.delete()
def __int__(self):
return self.match
But when I do that, nothing happens. What is required to get this to work?
Yoc can create a vote model and then proceed.
class Vote(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
value = models.BooleanField(default=True)
match = models.ForeignKey("MatchOpponents")
class Meta:
unique_together = (('user', 'match'),)

adding / editing django generic foreign key objects

Model Device:
class Device(models.Model):
device_code = models.CharField(max_length=64,unique=True)
is_enabled = models.BooleanField(default=False)
def __unicode__(self):
return u'%s: %s' % (self.device_code, 'ENABLED' if self.is_enabled else 'DISABLED')
Model AttributeValues:
class AttributeValue(models.Model):
attribute = models.ForeignKey(Attribute)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
class Meta:
abstract = True
unique_together = (
('attribute', 'content_type','object_id'),
)
index_together = (
('content_type','object_id'),
)
#property
def formatted(self):
"""
PLEASE SELECT RELATED ATTRIBUTE BEFORE USING THIS FUNCTION
"""
return self.attribute.format % self.value
def save(self,*args,**kwargs):
if hasattr(self.content_object,'invalidate_cache') and callable(self.content_object.invalidate_cache):
self.content_object.invalidate_cache()
super(AttributeValue,self).save(*args, **kwargs)
def __unicode__(self):
return u'%s %s' % (self.attribute.name, self.value)
class NumericAttributeValue(AttributeValue):
value = models.DecimalField(max_digits=12,decimal_places=4)
class LongTextAttributeValue(AttributeValue):
value = models.TextField()
class ShortTextAttributeValue(AttributeValue):
value = models.CharField(max_length=255)
class FileAttributeValue(AttributeValue):
attribute_file = models.FileField(upload_to="attribute_imgs")
Model Attribute:
ATTRIBUTE_TYPE_CHOICES = (
('n','Numeric'),
('s','Short Text (255)'),
('m','Long Text')
)
class Attribute(models.Model):
name = models.CharField(max_length=255)
code = models.CharField(max_length=64,unique=True)
attribute_type = models.CharField(max_length=1,choices=ATTRIBUTE_TYPE_CHOICES)
sorting_order = models.PositiveIntegerField(default=0)
show = models.BooleanField(default=False)
format = models.CharField(max_length=64,default='%s')
class Meta:
ordering = ['sorting_order','name']
def __unicode__(self):
return self.name
In my device editing (adding) page, it needs to be able to create or select an attribute, then create (or edit / delete) an attribute value (could be a numeric value, long text value, short text value or a file) associated to this attribute, and the current (or new) device. How would you create a django formset for this kind of scenario?
I had to solve a similar problem and django-polymorphic worked for me.
If you define an abstract model as the parent, then it allows you to select any child models that the parent is based on in the Django admin interface (when selecting a foreign-key for example).
You will have to make some changes in your model & admin to get it working (for eg; you won't need GenericForeignKey).
https://django-polymorphic.readthedocs.org/en/latest/

Django: Creating editable default values for model instance based on foreignkey existance

I'm playing around in Django, and wondering if there is a way to loop through instances of two different models I have created?
/ models.py:
class Tran(models.Model):
name = models.CharField(max_length=300)
description = models.CharField(max_length=2000)
type = models.ForeignKey(TransactionType)
def __str__(self):
return self.name
class DocLink(models.Model):
trann = models.ForeignKey(Transaction)
t_link = models.CharField(max_length=2000)
t_display = models.CharField(max_length=1000)
p_display = models.CharField(max_length=300)
p_link = models.CharField(max_length=2000)
def __str__(self):
return self.link
What I want to do:
Look through each of the Tran instances and create a default value for the links/displays in the DocLink table instead of doing it manually.
Is there anyway I can be pointed in the right direction?
If you want to set links/displays default value in DocLink instance based on trann field you can override model's save method.
For example following code shows how to set t_link if it doesn't have a value:
class DocLink(models.Model):
trann = models.ForeignKey(Transaction)
t_link = models.CharField(max_length=2000)
t_display = models.CharField(max_length=1000)
p_display = models.CharField(max_length=300)
p_link = models.CharField(max_length=2000)
def __str__(self):
return self.link
def save(self, *args, **kwargs):
if not self.t_link:
pass # TODO set self.t_link based on trann
super(DocLink, self).save(*args, **kwargs)
Also you can change model's trann field to:
trann = models.ForeignKey(Transaction, related_name="doclinks")
And then access to all DocLinks of a Tran with:
# t is an instance of Tran class
t.doclinks.all()
So you can loop through this list and do what you want.

Django model relationship - Fitness App

Currently my models are:
class Workout(models.Model):
date = models.DateField()
routine = models.ForeignKey('Routine')
def __str__(self):
return '%s' % self.date
class Routine(models.Model):
name = models.CharField(max_length=255)
exercises = models.ManyToManyField('Exercise')
def __str__(self):
return self.name
class Exercise(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return self.name
I want the user to be able to create a new entry specified by a date(Workout). They can also create routines(Routine), associated with the date and filled with different exercises(Exercise) which they can also create.
Here is the part I can't figure out.
I want the user, when adding a new exercise, to be able to choose whether it is a strength exercise or cardio exercise. Strength exercises will have fields like: #of sets, reps, and weight. Where as carido will have fields like length and speed.
I am unclear on how to relate the two types of exercises to the Exercise class.
The most common way of doing this, is to create a generic relationship, such as:
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
class Exercise(models.Model):
name = models.CharField(max_length=255)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
info = GenericForeignKey('content_type', 'object_id')
def __str__(self):
return self.name
class StrengthExercise(models.Model):
sets, reps, weight = (...)
class CardioExercise(models.Model):
length, speed = (...)
Example use:
>>> from app_name.models import Exercise, CardioExercise
>>> exercise_info = CardioExercise.objects.create(length=600, speed=50)
>>> exercise = Exercise(name="cardio_exercise_1", info=exercise_info)
>>> exercise.save()
>>> exercise.info.length
600
>>> exercise.info.__class__.__name__
'CardioExercise'
OBS: Make sure you have 'django.contrib.contenttypes' in your INSTALLED_APPS (enabled by default).

Adding dynamic fields to Django models

How do I create a dynamic field on a model?
Let's say I'm writing an application related to the stock market. I make a purchase on one day and sometime later I want to check the gain (or loss) based on today's price. I'd have a model like this:
class Purchase(models.Model):
ticker = models.CharField(max_length=5)
date = models.DateField()
price = models.DecimalField(max_digits=20, decimal_places=3)
quantity = models.IntegerField()
What I'd like to do is define a model something like this:
class PurchaseGain(Purchase):
gain = models.DecimalField(max_digits=20, decimal_places=3)
class Meta:
proxy = True
So that I could do this:
todays_price = get_price_from_webservice(ticker)
for p in PurchaseGain.objects.get_purchase_gain(todays_price):
print '%s bought on %s for a gain of %s' % (p.ticker, p.date, p.gain)
Where p.gain is dynamically computed based on the input to get_purchase_gain. Rather than just constructing dictionaries on the fly I want to use a model, because I'd like to pass this around and generate forms, save changes, etc from the instance.
I tried creating a derived QuerySet, but that led to a circular dependency, because Purchase needed to know about the QuerySet (through a custom manager) and the QuerySet returned an iterator that needed to instantiate a PurchaseGain, which was derived from Purchase.
What options do I have?
Thanks,
Craig
Why not add a gain() method to your model?
class Purchase(models.Model):
ticker = models.CharField(max_length=5)
date = models.DateField()
price = models.DecimalField(max_digits=20, decimal_places=3)
quantity = models.IntegerField()
def gain(self, todays_price=None):
if not todays_price:
todays_price = get_price_from_webservice(self.ticker)
result_gain = todays_price - self.price
return result_gain
Then you can pretty much do what you want:
for p in Purchase.objects.all():
print '%s bought on %s for a gain of %s' % (p.ticker, p.date, p.gain())
Creating a proxy class is what confused me. By just adding attributes to a Purchase, I was able to accomplish what I wanted.
class PurchaseQuerySet(QuerySet):
def __init__(self, *args, **kwargs):
super(PurchaseQuerySet, self).__init__(*args, **kwargs)
self.todays_price = None
def get_with_todays_price(self, todays_price):
self.todays_price = todays_price
cloned = self.all()
cloned.todays_price = todays_price
return cloned
def iterator(self):
for p in super(PurchaseQuerySet, self).iterator():
p.todays_price = self.todays_price
yield p
class PurchaseManager(models.Manager):
def get_query_set(self):
return PurchaseQuerySet(self.model)
def __getattr__(self, name)
return getattr(self.get_query_set(), name)
class Purchase(models.Model):
ticker = models.CharField(max_length=5)
date = models.DateField()
price = models.DecimalField(max_digits=20, decimal_places=3)
quantity = models.IntegerField()
objects = PurchaseManager()
#property
def gain(self):
return self.todays_price - self.price
Now I can do:
for p in Purchase.objects.filter(ticker=ticker).get_with_todays_price(100):
print p
print p.gain

Categories