As a follow up to my eariler question I have a new one. How can I save this calculated value as a model field. I would like to use it in my views and templates to order list by this field.
My models:
class Tournament(models.Model):
name = models.CharField(max_length=100)
date = models.DateTimeField('date')
player_num = models.IntegerField(verbose_name="")
points = models.FloatField(default=1000.00)
def get_rating(self):
return self.points / 1000.00
class TournamentStandings(models.Model):
tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE)
player = models.ForeignKey(Player, on_delete=models.CASCADE)
player_place = models.FloatField(verbose_name=u"")
player_points = models.FloatField(verbose_name="",
blank=True) #added for testing to save the calculated value in it
#property
def get_player_points(self, obj):
return obj.tournament.player_num * obj.tournament.get_rating() -
obj.tournament.get_rating()*(obj.player_place - 1.00)
def save(self, *args, **kwargs):
self.player_points = self.get_player_points
super(TournamentStandings, self).save(*args, **kwargs)
def __float__(self):
return self.player_points
Funny as on the admin list I have a column where player_points are calculated correctly but when I add a new model instance and try to save it I get this error : 'TournamentStandings' object has no attribute 'get_player_points'. Is it bacause I am trying to do a "self" save and my calculation is (self, obj) ?? Any hints are wellcome.
Posting a working solution to my problem. No need for parentheses.
First I have fixed Tournament model, so I could save get_rating as a field:
class Tournament(models.Model):
name = models.CharField(max_length=100)
rating = models.FloatField(verbose_name="Rating", blank=True)
#property
def get_rating(self):
return (self.points) / (1000.00)
def save(self, *args, **kwargs):
self.rating = self.get_rating
super(Tournament, self).save(*args, **kwargs)
def __float__(self):
return self.rating
When I had this I tried to copy it to second model. Problem was that I could not get it to work due to related obj I was calling in my calculation. But! I have managed to assign this values to variables inside get_player_points and now all is working as intended:
class TournamentStandings(models.Model):
tournament = models.ForeignKey(Tournament, on_delete=models.CASCADE)
player = models.ForeignKey(Player, on_delete=models.CASCADE)
player_place = models.FloatField(verbose_name="")
player_points = models.FloatField(verbose_name="", blank=True)
#property
def get_player_points(self):
player_num = float(self.tournament.player_num)
rating = float(self.tournament.rating)
player_points = float(rating*player_num-rating*(self.player_place - 1.00))
return player_points
def save(self, *args, **kwargs):
self.player_points = self.get_player_points
super(TournamentStandings, self).save(*args, **kwargs)
def __float__(self):
return self.player_points
And this works! Any thoughts on improvements I could make are wellcome ofc.
get_player_points() is a method and requires parentheses.
def save(self, *args, **kwargs):
self.player_points = self.get_player_points()
Related
Hello I have a function to auto generate data for my SlugField but i dont know how to implement a save method to execute it. Ive tried calling the function from the save method but it raises an error of 'instance not defined'. Any suggestions will help. Thanks.
def ran_str_gen(size=6, chars=string.ascii_letters + string.digits):
return ''.join(secrets.choice(chars) for s in range(size))
def slug_gen(instance, new_slug=None):
if new_slug is not None:
slug=new_slug
else:
slug = slugify(instance.title)
op = instance.__class__
qs_exists = op.objects.filter(slug=slug).exists()
if not qs_exists:
new_slug = '{slug}-{ranstr}'.format(slug=slug, ranstr=ran_str_gen())
return slug_gen(instance, new_slug=new_slug)
return slug
class Item(models.Model):
title = models.CharField(max_length=100)
price = models.FloatField()
slug = models.SlugField()
def save(self, *args, **kwargs):
slug_gen()
You should pass the instance (self) to the slug_gen function, store the result in the slug field, and then make a super() call to save the model effectively, so:
class Item(models.Model):
title = models.CharField(max_length=100)
price = models.FloatField()
slug = models.SlugField()
def save(self, *args, **kwargs):
self.slug = slug_gen(self)
super().save(*args, **kwargs)
Note: You can make use of django-autoslugĀ [GitHub] to automatically create a slug based on other field(s).
Note: Normally you should not change slugs when the related fields change. As is written in the article Cool URIs don't changeĀ [w3.org], URIs are not supposed to change, since these can be bookmarked. Therefore the slug should only be created when creating the object, not when you change any field on which the slug depends.
def save(self, *args, **kwargs):
self.slug=slug_gen()
I am facing a problem in which I cannot save documents inside MongoDB with Django.
The error follows:
AttributeError: 'NoneType' object has no attribute 'attname'
With the help of the library Djongo I made these models:
from djongo import models
from django.utils import timezone
class LatLng(models.Model):
latitude = models.FloatField(null=False)
longitude = models.FloatField(null=False,)
def __init__(self,latitude, longitude):
self.latitude = latitude
self.longitude = longitude
class Meta:
abstract = True
class Parameters(models.Model):
cond1= models.IntegerField(null=False,)
cond2= models.IntegerField(null=False,)
cond3= models.IntegerField()
class Meta:
abstract = True
class MyModel(models.Model):
name = models.CharField(max_length=150, null=False)
des= models.CharField(max_length=500)
pub_date = models.DateTimeField(editable=False)
mod_date = models.DateTimeField()
parameters = models.EmbeddedField(
model_container=Parameters
)
wp= models.ArrayField(
model_container=LatLng,
null=False
)
objects = models.DjongoManager()
def __init__(self, name, parameters, wp,des=""):
self.name = name
self.parameters = parameters
self.waypoints = waypoints
def save(self, *args, **kwargs):
''' On save, update timestamps '''
if self.id is None:
self.pub_date = timezone.now()
self.mod_date = timezone.now()
return super(MyModel, self).save(*args, **kwargs)
def __str__(self):
return self.name
My API looks like:
def get_wp(pls):
wps = []
for pin pls:
coord = LatLng(latitude=p['latitude'], longitude=p['longitude'])
wps.append(coord)
return wps
#api_view(['POST'])
def save(request):
data = json.loads(request.body.decode('utf-8'))
scores = Parameters(cond1=data['cond1'], cond2=data['cond2'])
wps = get_wp(data['pls'])
obj = MyModel(name=data['name'],parameters=scores, waypoints=wps)
print("--> {}".format(obj.name)) #working fine
itinerary.save() ## it dies here
return JsonResponse({})
I don't know what I'm making wrong. Since this is my first time with Django (using MongoDB), any suggestions about my code are really appreciated.
Try removing LatLng __init__() or if still needed then try:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Do your changes here
I'm new to Django I got an issue. I don't know how to retrieve the current post inside of models.py. I've tried different way for that.
'QuerySet' object has no attribute 'aliments'
or no error and no add to Post from ListAliments
get_object_or_404(Post, id=kwargs['id'])
here is my models.py
class ListAliments(models.Model):
name = models.CharField(max_length=40, unique=True)
slug = models.SlugField(editable=False)
status = models.IntegerField(choices=STATUS, default=1)
def save(self, *args,**kwargs):
if not self.slug:
self.slug = unique_slugify(self, slugify(self.name))
super(ListAliments, self).save(*args, **kwargs)
def __str__(self):
return self.name
class Post(models.Model):
title = models.CharField(max_length=190)
url_image = models.URLField(max_length=200, default=None)
aliments = models.ManyToManyField('ListAliments',blank=True, related_name='listaliments_post')
...
def save(self, *args, **kwargs):
if not self.slug:
self.slug = unique_slugify(self, slugify(self.title))
super(Post, self).save(*args, **kwargs) -> First save for Post which has not no ID
...
if self.url_image:
request = ...
response = ...
if response:
names = []
for concept in response.outputs[0].data.concepts:
current_aliments = ListAliments.objects.filter(name=concept.name)
current_post = Post.objects.filter(url_image=self.url_image) #get_object_or_404(Post, id=kwargs['id'])
if current_aliments.count()<1:
create_aliments = self.aliments.create(name=concept.name)
current_post.aliments.add(create_aliments)
else:
existed_aliments = ListAliments.objects.get(name=concept.name)
current_post.aliments.add(existed_aliments)
super().save(*args, **kwargs)
Post.objects.filter(url_image=self.url_image) returns QuerySet
in order to get object call first so Post.objects.filter(url_image=self.url_image).first(); note that you can get None
So I have Transaction model that is FK-d to a Share. In an 'Account' view, I have a ModelFormset of these Transactions and I can save multiple transactions by looping through the forms and saving them.
On my Transaction's save() method I try and update the balance on the linked Share. this works if I save one transaction, but when I POST my ModelFormset with multiple transactions, everytime I hit the self.share.balance = self.share.balance + amt line in the Transaction save() override (that is for every new Transaction), the share.balance is what it was before any of the previous transactions in the formset were saved.
Does anyone know why the added amount to share balance from a previous saved transaction is not carried on the subsequent saves (why only the last Transaction's amount will be added to share balance)?
Transaction model which should update balance on parent-model Share
class Transaction(models.Model):
share = models.ForeignKey(Share, on_delete=models.CASCADE, null=True, blank=True)
account = models.ForeignKey(Account, on_delete=models.CASCADE, null=True, blank=True)
db_cr = models.CharField(choices=DBCR, max_length=2)
amt = models.DecimalField('Amount', max_digits=11, decimal_places=2)
post_dt = models.DateTimeField('Post Time', null=True, blank=True)
def save(self, *args, **kwargs):
if not self.pk:
...
if self.share:
if self in self.share.transaction_set.all():
logging.error('Transaction %s already posted' % self.id)
return False
amt = self.amt if self.db_cr == 'cr' else -self.amt
self.share.balance = self.share.balance + amt
self.share.save()
Share Model
class Share(models.Model):
name = models.CharField(max_length=80)
account = models.ForeignKey(Account, on_delete=models.CASCADE)
definition = models.ForeignKey(ShareDef, on_delete=models.PROTECT)
balance = models.DecimalField('Balance', max_digits=11, decimal_places=2, default=0)
def __str__(self):
return '%s %s %s %s'%(self.account,
self.name,
self.definition.sym_code,
self.balance )
def save(self, *args, **kwargs):
if not self.pk:
if not self.name:
self.name = self.definition.name
super(Share, self).save(*args, **kwargs)
In view, I have a Transaction formset
#...in view
TranFormSet = modelformset_factory(Transaction, exclude=('origin','ach_entry'), extra=1)
if request.method=='POST':
...
tran_formset = TranFormSet(request.POST)
...
if tran_formset.is_valid():
for form in tran_formset:
tran = form.save(commit=False)
tran.account = account
tran.origin = 'tt'
tran.save()
else:
#...following kind of weird because of how I'm setting querysets of ModelChoiceFields
kwargs = {'account_instance': account}
tran_formset = TranFormSet(queryset=Transaction.objects.none())
tran_formset.form = (curry(TranForm, **kwargs))
Form
class TranForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
account_instance = kwargs.pop('account_instance', None)
super(TranForm, self).__init__(*args, **kwargs)
if account_instance:
self.fields['share'].queryset = account_instance.share_set.all()
if self.instance.pk:
del self.fields['share']
class Meta:
model=Transaction
exclude=['origin', 'ach_entry', 'account']
post_dt = forms.DateTimeField(initial=datetime.date.today(), widget=forms.TextInput(attrs=
{
'class': 'datepicker'
}))
share = forms.ModelChoiceField(empty_label='---------', required=False, queryset=Share.objects.all())
It's unclear what may be causing the issue, but it may be helpful to perform the update of the self.share.balance in a single update() query. This can be done using F expressions:
from django.db.models import F
class Transaction(models.Model):
# ...
def update_share_balance(self):
if self.db_cr == "cr":
amount = self.amt
else:
amount = -self.amt
# By using the queryset update() method, we can perform the
# change in a single query, without using a potentially old
# value from `self.share.balance`
return Share.objects.filter(id=self.share_id).update(
balance=F("balance") + amount
)
def save(self, *args, **kwargs):
if not self.pk:
# ...
if self.share:
# ...
self.update_share_balance()
# Also, be sure to call the super().save() method at the end!
super().save(*args, **kwargs)
Django 1.8 I'm trying to get total price for the products in m2m field.
from django.models.db import Sum
class Product(models.Model):
...
price = models.DecimalField(default=0, max_digits=9, decimal_places=2)
class Order(models.Model):
...
products = models.ManyToManyField(Product, related_name='orders')
total = models.DecimalField()
...
def save(self, *args, **kwargs):
self.total = self.products.all().annotate(Sum('price'))
super(Order, self).save(*args, **kwargs)
When I try to save the Order object, the code above produce ValueError:
"<Order: None>" needs to have a value for field "order" before this
many-to-many relationship can be used.
Do you have specific requirements to save this total in your database? If not,
class Order(models.Model):
...
products = models.ManyToManyField(Product, related_name='orders')
...
#property
def total(self):
result = self.products.aggregate(Sum('price'))
return result['price__sum']
the best way to do it, it's by a signal:
https://docs.djangoproject.com/en/1.8/ref/signals/#m2m-changed
here it's a example:
#receiver(m2m_changed, sender=BusquedaInmueble.zonas.through)
def post_save_busqueda_m2m (sender, action, instance, *args, **kwargs):
busqueda = instance
if action == 'post_add':