Django Unique Slug by id - django

class Product(models.Model):
title = models.CharField(max_length=75)
class Deal(models.Model):
product = models.ForeignKey(Product)
slug = models.SlugField(max_length=255, unique=True)
Having a similar basic setup as above, I want to generate unique slugs for each Deal instance using product title of it's deal and id of the deal itself. IE: "apple-iphone-4s-161" where 161 is the id of the deal and the text before is the title of the product.
For this, how can I overwrite the save() method of the Deal model to apply it?

Of course you can simply overwrite save() method on model (or make receiver for post_save signal).
It will be something like:
from django.template.defaultfilters import slugify
class Deal(models.Model):
product = models.ForeignKey(Product)
slug = models.SlugField(max_length=255, unique=True)
def save(self, *args, **kwargs):
super(Deal, self).save(*args, **kwargs)
if not self.slug:
self.slug = slugify(self.product.title) + "-" + str(self.id)
self.save()
But what is ugly in this solution is that it will hit database twice (it is saved two times). It is because when creating new Deal object it will not have id until you save it for the first time (and you cannot do much about it).

i've bumped at this problem and tested the jasisz solution, and got the max recursion depth exceeded error, so i've fiddle it little and this is how looks for me:
def save(self, *args, **kwargs):
if not self.id:
self.slug = slugify(self.title)
super(Node, self).save(*args, **kwargs)
You could edit this to suit your needs, it tests if this records exists, if not then it creates the slug field otherwise is update and no need for modifieng the slug field.
hope it helps.

You should not have your id in that slug field at all. The two main reasons for this are:
It creates the problem you are having
It's redundant data in the database – the id is already stored
But there is a simple solution to your problem: The only reason why the slug is stored in the database is so you can find a row by slug. But you don't need that – you have the ID. So you should do this:
class DealManager(models.Manager):
def get_by_slug(slug):
return self.get(id=slug.rsplit('-', 1)[1])
class Deal(models.Model):
product = models.ForeignKey(Product)
objects = DealManager()
#property
def slug(self):
return slugify(f'{self.name}-f{self.id}')
In your view or wherever you need to retrieve an item given a slug, you can just do Deal.objects.get_by_slug(slug).

I know this may not exactly work for your situation, but I remember bumping into a similar case. I think I had a created_at field in my model that has auto_now=True. Or something simillar
My slug would look like like this
self.slug = '%s-%s' %(
slugify(self.title),
self.created_at
)
or you can have
self.slug = '%s-%s' %(
slugify(self.title),
datetime.datetime.now()
)
just make sure that the slug max_length is long enough to include the full created_at time, as well as the title, so that you don't end up with non-unique or over max length exceptions.

Related

How to update model field after saving instance?

I have a Django model Article and after saving an instance, I want to find the five most common words (seedwords) of that article and save the article in a different model Group, based on the seedwords.
The problem is that I want the Group to be dependent on the instances of Article, i.e. every time an Article is saved, I want Django to automatically check all existing groups and if there is no good fit, create a new Group.
class Article(models.Model):
seedword_group = models.ForeignKey("Group", null=True, blank=True)
def update_seedword_group(self):
objects = News.objects.all()
seedword_group = *some_other_function*
return seedword_group
def save(self, *args, **kwargs):
self.update_seedword_group()
super().save(*args, **kwargs)
class Group(models.Model):
*some_fields*
I have tried with signals but couldn't work it out and I'm starting to think I might have misunderstood something basic.
So, to paraphrase:
I want to save an instance of a model A.
Upon saving, I want to create or update an existing model B depending on A via ForeignKey.
Honestly I couldn't understand the rationale behind your need but I guess below code may help:
def update_seedword_group(content):
objects = News.objects.all()
"""
process word count algorithm and get related group
or create a new group
"""
if found:
seedword_group = "*some_other_function*"
else:
seedword_group = Group(name="newGroup")
seedword_group.save()
return seedword_group
class Group(models.Model):
*some_fields*
class Article(models.Model):
seedword_group = models.ForeignKey("Group", null=True, blank=True)
content = models.TextField()
def save(self):
self.group = update_seedword_group(self.content)
super().save()

How to limit add to a model in django

How to limit add to a model in django ? i would like to create only one company on this model, only one, so if user want to add more, it s not possible.
class Company(models.Model):
name = models.CharField(max_length=64)
address = models.TextField(max_length=250)
One of solutions is to override save method on Company
class Company(models.Model):
name = models.CharField(max_length=64)
address = models.TextField(max_length=250)
def save(self, *args, **kwargs):
if Company.objects.exists():
return
super().save(*args, **kwargs)
but your question is rather short and not precise so that's probably not the way you want it to behave

Django - Keep latest record of User

I want to keep the latest record of a user:
class Record(models.Model):
name = models.ForeignKey(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to='site_pics')
date = models.DateTimeField(null=True, blank=True, auto_now_add=True)
def save(self, *args, **kwargs):
super(Record, self).save(*args, **kwargs)
numdata = Record.objects.select_related('name').count()
print(numdata)
#if numdata > 1:
# Record.objects.select_related('name').first().delete()
As per this post Filter latest record in Django, I tried:
.distinct()
.select_related()
.prefetch_related()
None of them return the correct number of records or don't work at all because I'm using SQLite.
Thank you for any suggestions
In that case it might be better to change the modeling to a OneToOneField, such that the database will reject a second Record for the same user. You might also want to change the name of the relation to user, since you are referring to a user object, not its name.
In the save(…) method, you can look if the primary key is not yet filled in, if that is the case, you can delete the original record of the user. Regardless whether that exists or not. If this records does not exist, then it will act as no-op, where nothing changes in the database:
class Record(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to='site_pics')
date = models.DateTimeField(null=True, blank=True, auto_now_add=True)
def save(self, *args, force_insert=False, **kwargs):
if self.pk is None or force_insert:
Record.objects.filter(user_id=self.user_id).delete()
return super().save(*args, force_insert=force_insert, **kwargs)
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

Django form save or model save with a select field?

So I'm still new to Django. I have a single field in my form. And I was just wondering whether or not I need a form save function or a model save function? When is it appropriate to use either or?
For instance: My form:
class OpinionStatusForm(forms.Form):
choices = (('0', "Your Status"), ('1', "This"), ('2', "That"), ('3', "The Other"))
status = forms.CharField(max_length=2, widget=forms.Select(choices=choices, attrs={'class':'status_dropdown','onchange': 'this.form.submit();'}), required=False)
def save(self, opinion_status):
opinion_status.status = self.cleaned_data['status']
My model:
class OptionStatus(models.Model):
user = models.ForeignKey(User, null=True, unique=True)
status = models.CharField(max_length=2, choices=opinion_statuses)
opinion = models.ForeignKey(Opinion, null=True, blank=True)
def __unicode__(self):
return self.status
def save(self, *args, **kwargs):
super(OpinionStatus, self).save(*args, **kwargs)
I'm going to be ajax-ing the form. I don't know if that makes a difference or not. Thanks!
What you actually need, is a ModelForm. In your example you're working with a standard forms.Form. This is not bound to a model instance. As a result, there's also no need for a save method. The best examples are really given inside the Django docs:
https://docs.djangoproject.com/en/dev/topics/forms/modelforms/
Go step by step over the code examples and you'll understand. It would be too much to explain it all in one Stackoverflow answer - and the Django docs are incredible thorough.

Populating default value from another table

At a bit of a loss here. Basically I have a Product page in the admin section and at the bottom of that I have an TabularInline that let's me choose sizes for the product and set a price. This works but I'm trying to get it so that it pulls the default price from the Sizes table to be the initial value.
I tried the following:
class Size(models.Model):
name = models.CharField(max_length=50)
default_price = models.DecimalField(max_digits=9, decimal_places=2)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'sizes'
ordering = ['-created_at']
verbose_name_plural = 'Sizes'
def __unicode__(self):
return self.name
def default_price(self):
return self.default_price
And then
class ProductOption(models.Model):
product = models.ForeignKey(Product)
size = models.ForeignKey(Size)
price = models.DecimalField(max_digits=9, decimal_places=2, default=Size.default_price)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now_add=True)
It validates, but when I load the admin page I get the following:
unbound method get_default_price() must be called with Size instance as first argument (got nothing instead)
Not sure where else to go from here. I've been looking over the Django docs forever trying to find something else to try. Any ideas?
So, you can override the save method, and if there is no self.id, you can populate some other fields first, that's what sticks out at me as the best solution.
just as an example:
def save(*args, **kwargs):
if not self.id:
self.size = SomeOtherModel.objects.get(pk=size)
super(Size, self).save(*args, **kwargs)
You have several issues here:
Size.default_price is a callable object, so python is trying to call it when setting the default value of your ProductOption. What you might be looking for is:
class Size(models.Model):
# Other stuff here
#property
def default_price(self):
return self.default_price
However, this then causes another, deeper error. If you had the following:
size = Size()
size.default_price
You would wind up with a StackOverflow, due to infinite recursion. This property calls itself. A solution is to have the property name different to the attribute, or in this simplest case, it already acts a bit like a property. You can just remove the whole default_price definition.
However, you are calling Size.default_price, which is not bound to anything if you call it on Size, a class, not on an instance of that class.
What you want to actually do is kind of what Issac Kelly suggests, but more so.
class ProductOption(models.Model):
# column definitions
def __init__(self, *args, **kwargs):
super(ProductOption, self).__init__(*args, **kwargs)
if self.size and not self.price:
self.price = self.size.default_price
This means that when you instantiate a ProductOption, it will automatically set the price based on the size, if there isn't already a price.
You might also want to do this check at save time, as otherwise:
po = ProductOption()
po.size = Size.objects.get(pk=1)
po.save()
# po.price is not set now!
po = ProductOption.objects.get(pk=po.pk)
# po.price is set now!
So, you could have the following:
class ProductOption(models.Model):
# columns
def __init__(self, *args, **kwargs):
super(ProductOption, self).__init__(*args, **kwargs)
self.set_default_price()
def save(self, *args, **kwargs):
self.set_default_price()
super(ProductOption, self).save(*args, **kwargs)
def set_default_price(self):
if self.size and not self.price:
self.price = self.size.default_price
Note the difference in order of the super() call: when initialising the object, we want to call the parent's __init__ first, but when saving, we want to (possibly) set the default value first, then call the parent class's save method.