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
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()
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()
I've a product-stock model as given below.
TRANSACTION_TYPE=(('I','Stock In'),('O','Stock Out'))
class Stock(models.Model):
product=models.ForeignKey('product.Product', blank=False,null=False)
date=models.DateField(blank=False, null=False,)
quantity=models.PositiveIntegerField(blank=False, null=False)
ttype=models.CharField(max_length=1,verbose_name="Transaction type",choices=TRANSACTION_TYPE, blank=False, db_index=True)
I need to log the update activity on this model, along with the id of the user who updated it.
ACTIONS=(('EC','Edit Category'),
('EG','Edit Group'),
('EP','Edit Product'),
('ES','Edit Stock'))
class MyLog(models.Model):
user=models.ForeignKey(auth.models.User, blank=False)
action= models.CharField(max_length=2, choices=ACTIONS, null=False,blank=False)
date=models.DateTimeField(blank=False, auto_now=True)
data = JSONField()
I've the added following code to the Stock model.
def __init__(self, *args, **kwargs):
super(Stock, self).__init__(*args, **kwargs)
if self.pk != None :
self.__important_fields = ['product','date', 'quantity', 'ttype', ]
for field in self.__important_fields:
setattr(self, '__original_%s' % field, getattr(self, field))
field_name='__original_%s' % field
def save(self, *args, **kwargs):
if self.pk != None :
print("Editing")
flag=False
log=MyLog(user=?,action='ES')
log.data=[]
for field in self.__important_fields:
original=getattr(self, '__original_%s' % field)
if original != getattr(self, field):
flag=True
log.data.append({field : str(original)})
if flag:
log.save()
else:
print("Adding")
super(Stock, self).save(*args, **kwargs)
This works, when I hard code a user object into the line log=MyLog(user=?,action='ES').
I need to log the id of the user who performed this edit operation.
How can I achieve this?
Thanks.
Here's how I finally achieved my goal.
Instead of logging the event from the model, I switched my code to the forms.
Here's my final code.
mylog app model
ACTIONS=(('EC','Edit Category'),
('EG','Edit Group'),
('EP','Edit Product'),
('ES','Edit Stock'))
class MyLog(models.Model):
user=models.ForeignKey(settings.AUTH_USER_MODEL, blank=False)
model_id=models.IntegerField(default=0)
action= models.CharField(max_length=2, choices=ACTIONS, null=False,blank=False)
date=models.DateTimeField(blank=False, auto_now=True)
old_data = JSONField(default=None)
new_data = JSONField(default=None)
stock app - update view
class UpdateStock(UpdateView):
model=Stock
form_class=UpdateStockForm
def get_form_kwargs(self):
kwargs = super( UpdateStock, self).get_form_kwargs()
kwargs.update({'user_id': self.request.user.id})
return kwargs
stock app - update form
class UpdateStockForm(forms.ModelForm):
def __init__(self,pk= None, *args, **kwargs):
self.user_id=kwargs.pop('user_id')
super(UpdateStockForm, self).__init__(*args, **kwargs)
def clean(self):
cleaned_data = super(UpdateStockForm, self).clean()
quantity = cleaned_data.get('quantity')
date= cleaned_data.get('date')
priv_quantity=self.instance.quantity
priv_date=self.instance.date
if priv_quantity!=quantity or priv_date != date:
#log!
log=MyLog(user=auth.models.User.objects.get(pk=self.user_id),action='ES', model_id=self.instance.id)
log.old_data=[]
log.old_data.append({'date' : str(priv_date), 'quantity':priv_quantity })
log.new_data=[]
log.new_data.append({ 'date' : str(date), 'quantity':quantity })
log.save()
return cleaned_data
I have a model, which contains an ImageField. I want to autocreate 2 additional fields from that ImageField.
class Images(models.Model):
...
img = models.ImageField(upload_to='img')
#for autocreating
rimg = models.ImageField(upload_to='rimg', null=True, blank=True)
limg = models.ImageField(upload_to='limg', null=True, blank=True)
def create_rimg(self):
# some work for generating rimg
self.rimg.save()
def create_limg(self):
# some work for generating limg
self.limg.save()
def save(self, *args, **kwargs):
self.create_rimg()
self.create_limg()
force_update = False
if self.id:
force_update = True
super(Images, self).save(force_update=force_update, *args, **kwargs)
That code gives a 500 error. If i call that code, it works perfect:
def save(self, *args, **kwargs):
self.create_rimg()
#self.create_limg()
force_update = False
or
def save(self, *args, **kwargs):
#self.create_rimg()
self.create_limg()
force_update = False
How can i call that 2 functions in save() method?
I am trying to create a unique slug in Django so that I can access a post via a url like this:
http://www.example.com/buy-a-new-bike_Boston-MA-02111_2
The relevant models:
class ZipCode(models.Model):
zipcode = models.CharField(max_length=5)
city = models.CharField(max_length=64)
statecode = models.CharField(max_length=32)
class Need(models.Model):
title = models.CharField(max_length=50)
us_zip = models.CharField(max_length=5)
slug = ?????
def get_city():
zip = ZipCode.objects.get(zipcode=self.us_zip)
city = "%s, %s %s" % (zip.city, zip.statecode, zip.zipcode)
return city
A sample ZipCode record:
zipcode = "02111"
city = "Boston"
statecode = "MA"
A sample Need record:
title = "buy a new bike"
us_zip = "02111"
slug = "buy-a-new-bike_Boston-MA-02111_2" (desired)
Any tips as to how to create this unique slug? Its composition is:
Need.title + "_" + Need.get_city() + "_" + an optional incrementing integer to make it unique. All spaces should be replaced with "-".
NOTE: My desired slug above assumes that the slug "buy-a-new-bike_Boston-MA-02111" already exists, which is what it has the "_2" appended to it to make it unique.
I've tried django-extensions, but it seems that it can only take a field or tuple of fields to construct the unique slug. I need to pass in the get_city() function as well as the "_" connector between the title and city. Anyone solved this and willing to share?
Thank you!
UPDATE
I'm already using django-extensions for its UUIDField, so it would be nice if it could also be usable for its AutoSlugField!
I use this snippet for generating unique slug and my typical save method look like below
slug will be Django SlugField with blank=True but enforce slug in save method.
typical save method for Need model might look below
def save(self, **kwargs):
slug_str = "%s %s" % (self.title, self.us_zip)
unique_slugify(self, slug_str)
super(Need, self).save(**kwargs)
and this will generate slug like buy-a-new-bike_Boston-MA-02111 , buy-a-new-bike_Boston-MA-02111-1 and so on. Output might be little different but you can always go through snippet and customize to your needs.
My little code:
def save(self, *args, **kwargs):
strtime = "".join(str(time()).split("."))
string = "%s-%s" % (strtime[7:], self.title)
self.slug = slugify(string)
super(Need, self).save()
If you are thinking of using an app to do it for you, here is one.
https://github.com/un33k/django-uuslug
UUSlug = (``U``nique + ``U``code Slug)
Unicode Test Example
=====================
from uuslug import uuslug as slugify
s = "This is a test ---"
r = slugify(s)
self.assertEquals(r, "this-is-a-test")
s = 'C\'est déjà l\'été.'
r = slugify(s)
self.assertEquals(r, "c-est-deja-l-ete")
s = 'Nín hǎo. Wǒ shì zhōng guó rén'
r = slugify(s)
self.assertEquals(r, "nin-hao-wo-shi-zhong-guo-ren")
s = '影師嗎'
r = slugify(s)
self.assertEquals(r, "ying-shi-ma")
Uniqueness Test Example
=======================
Override your objects save method with something like this (models.py)
from django.db import models
from uuslug import uuslug as slugify
class CoolSlug(models.Model):
name = models.CharField(max_length=100)
slug = models.CharField(max_length=200)
def __unicode__(self):
return self.name
def save(self, *args, **kwargs):
self.slug = slugify(self.name, instance=self)
super(CoolSlug, self).save(*args, **kwargs)
Test:
=====
name = "john"
c = CoolSlug.objects.create(name=name)
c.save()
self.assertEquals(c.slug, name) # slug = "john"
c1 = CoolSlug.objects.create(name=name)
c1.save()
self.assertEquals(c1.slug, name+"-1") # slug = "john-1"
Here are a couple functions that I use. You pass in the model instance and the desired title into unique_slugify which will add the slug if it doesn't exist, otherwise it will continue trying to append a 4 digit random string until it finds a unique one.
import string
from django.utils.crypto import get_random_string
def unique_slugify(instance, slug):
model = instance.__class__
unique_slug = slug
while model.objects.filter(slug=unique_slug).exists():
unique_slug = slug + get_random_string(length=4)
return unique_slug
I usually use it by overriding the model save method.
class YourModel(models.Model):
slug = models.SlugField()
title = models.CharField()
def save(self, *args, **kwargs):
if not self.slug:
self.slug = unique_slugify(self, slugify(self.title))
super().save(*args, **kwargs)
This is the simple and small code i am using for generating unique slug,
you only need one field to create your unique slug field
from random import randint
def save(self, *args, **kwargs):
if Post.objects.filter(title=self.title).exists():
extra = str(randint(1, 10000))
self.slug = slugify(self.title) + "-" + extra
else:
self.slug = slugify(self.title)
super(Post, self).save(*args, **kwargs)
I hope you like this.
This is a simple implementation that generate the slug from the title, it doesn't depend on other snippets:
from django.template.defaultfilters import slugify
class Article(models.Model):
...
def save(self, **kwargs):
if not self.slug:
slug = slugify(self.title)
while True:
try:
article = Article.objects.get(slug=slug)
if article == self:
self.slug = slug
break
else:
slug = slug + '-'
except:
self.slug = slug
break
super(Article, self).save()
Django provides a SlugField model field to make this easier for you. Here's an example of it in a "blog" app's
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField(blank=True)
slug = models.SlugField(unique=True)
#models.permalink
def get_absolute_url(self):
return 'blog:post', (self.slug,)
Note that we've set unique=True for our slug field — in this project we will be looking up posts by their slug, so we need to ensure they are unique. Here's what our application's views.py might look like to do this:
from .models import Post
def post(request, slug):
post = get_object_or_404(Post, slug=slug)
return render(request, 'blog/post.html', {
'post': post,
})
from django.utils.text import slugify Helps a lot and has quite clear Concepts.
Here one example on How to auto-generate slug by using from django.utils.text import slugify
utils.py
from django.utils.text import slugify
import random
import string
# Random string generator
def random_string_generator(size=10, chars=string.ascii_lowercase + string.digits):
return ''.join(random.choice(chars) for _ in range(size))
# Unique Slug Generator
def unique_slug_generator(instance, new_slug=None):
"""
It assumes your instance has a model with a slug field and a title character (char) field.
"""
if new_slug is not None:
slug = new_slug
else:
slug = slugify(instance.title)
Klass = instance.__class__
qs_exists = Klass.objects.filter(slug=slug).exists()
if qs_exists:
new_slug = "{slug}-{randstr}".format(slug=slug, randstr=random_string_generator(size=4))
return unique_slug_generator(instance, new_slug=new_slug)
return slug
models.py
from django.db.models.signals import pre_save # Signals
# import the unique_slug_generator from .utils.py
from .utils import unique_slug_generator
class Product(models.Model):
title = models.CharField(max_length=120)
# set blank to True
slug = models.SlugField(blank=True, unique=True)
def product_pre_save_receiver(sender, instance, *args, **kwargs):
if not instance.slug:
instance.slug = unique_slug_generator(instance)
pre_save.connect(product_pre_save_receiver, sender=Product)
Django documentation explains Django.utils.text import slugify to generate slug automatically. You can read more detail here
After implementing the code, while creating product, you may leave the slug field blank, which will be further aquired with auto generated slug for the product which will be unique in this case.
Hi can you tried this function
class Training(models.Model):
title = models.CharField(max_length=250)
text = models.TextField()
created_date = models.DateTimeField(
auto_now_add=True, editable=False, )
slug = models.SlugField(unique=True, editable=False, max_length=250)
def __unicode__(self):
return self.title
def get_unique_slug(id,title,obj):
slug = slugify(title.replace('ı', 'i'))
unique_slug = slug
counter = 1
while obj.filter(slug=unique_slug).exists():
if(obj.filter(slug=unique_slug).values('id')[0]['id']==id):
break
unique_slug = '{}-{}'.format(slug, counter)
counter += 1
return unique_slug.
def save(self, *args, **kwargs):
self.slug =self.get_unique_slug(self.id,self.title,Training.objects)
return super(Training, self).save(*args, **kwargs)
def get_slug(self):
slug = slugify(self.title.replace("ı", "i"))
unique = slug
number = 2
while Model.objects.filter(slug=unique).exists():
unique = "{}-{}".format(slug, number)
number += 1
return unique
Best solution for me:
def get_slug(self):
slug = slugify(self.title)
unique_slug = slug
number = 1
while Recipe.objects.filter(slug=unique_slug).exists():
unique_slug = f'{slug}-{number}'
number += 1
return unique_slug
def save(self, *args, **kwargs):
if not self.slug:
self.slug = self.get_slug()
return super().save(*args, **kwargs)
This code can generate slug like this:
string-slug
string-slug-1 (if previous alredy exists)
string-slug-2 (if previous alredy exists)
and so on...
class Need(models.Model):
title = models.CharField(max_length=50)
us_zip = models.CharField(max_length=5)
slug = models.SlugField(unique=True)
def save(self, **kwargs):
slug_str = "%s %s" % (self.title, self.us_zip)
super(Need, self).save()
Try this, worked out for me,welcome in advance:
class Parcel(models.Model):
title = models.CharField(max_length-255)
slug = models.SlugField(unique=True, max_length=255)
weight = models.IntegerField()
description = models.CharField(max_length=255)
destination = models.CharField(max_length=255)
origin = models.CharField(max_length=255)
def __str__(self):
return self.description
def save(self, *args, **kwargs):
if not self.slug:
t_slug = slugify(self.title)
startpoint = 1
unique_slug = t_slug
while Parcel.objects.filter(slug=unique_slug).exists():
unique_slug = '{} {}'.format(t_slug, origin)
origin += 1
self.slug = unique_slug
super().save(*args, **kwargs)