How to create slug of many to many fields in django? - django

I want to make slugs on post save for my model using three fields one charfield and two ManytoMany Fields but getting error during saving.
Here is my code:
Heading ##class product(models.Model):
id = models.AutoField(primary_key=True)
image = models.ForeignKey(all_images, verbose_name='Product Images', on_delete=models.CASCADE, related_name='proimages')
ProductName = models.CharField(max_length=200, null=False,default="None", blank=False,verbose_name="Product Name")
CategoryName = models.ManyToManyField(category,related_name='procat', blank=False,verbose_name="Category Name")
SubcatName = models.ManyToManyField(subcategory,related_name='prosubcat', blank=False,verbose_name="Sub-category Name")
description = RichTextUploadingField(blank= False,verbose_name="Description")
price = models.IntegerField(default=100, null=True, blank=True, verbose_name='Price')
slug = models.SlugField(max_length=55, blank=True, null=True)
def get_slug(self):
slug = self.ProductName
try:
for items in self.SubcatName.all():
slug +=items.name
try:
for items in self.CategoryName.all():
slug +=items.CategoryName
except:
pass
except:
pass
return slugify(slug)
def save(self, *args, **kwargs):
if not self.slug:
self.slug = self.get_slug()
super(product, self).save(*args, **kwargs)

Related

Django: using __str__ with a foreignkey in order to return an item

I am simply trying to override the str method to return something from a foreign key. A snippet of my models is as follows:
class Product(models.Model):
name = models.CharField(max_length=120)
shortened_name = models.CharField(max_length=50)
date_of_creation = models.CharField(max_length=20, null=True)
history = models.CharField(max_length=255)
price = models.DecimalField(max_digits=9, decimal_places=2)
style = models.CharField(max_length=50)
artist = models.ForeignKey(Artist, on_delete=models.SET_NULL, null=True)
image = models.ImageField(upload_to='images', null=True)
slug = models.SlugField()
def get_absolute_url(self):
return reverse('product', args=[self.slug])
def save(self, *args, **kwargs):
self.slug = slugify(self.shortened_name)
super().save(*args, **kwargs)
def get_add_to_cart_url(self):
return reverse('add_to_cart', kwargs={
'slug': self.slug
})
class ShoppingCartOrderItem(models.Model):
item = models.ForeignKey(Product, on_delete=models.CASCADE)
quantity = models.IntegerField(default=1)
def __str__(self):
return self.product.name
After adding the item to my shopping cart, in the admin panel when trying to view the specific item I get an AttributeError: 'ShoppingCartOrderItem' object has no attribute 'product'. Based on my googling so far I don't see why this isn't working. I have even tried str(self.product.name) but still the same error produces. I am unsure what I am doing wrong. Any help or suggestions is greatly appreciated!

Slugify self.title + random numbers

I hope you're well. I've two questions for you:
class Post(models.Model):
title = models.CharField(max_length=200, unique=True)
slug = models.SlugField(max_length=200, unique=True)
url_image = models.URLField(max_length=200, default='SOME STRING')
author = models.ForeignKey(User, on_delete= models.CASCADE,related_name='blog_posts')
updated_on = models.DateTimeField(auto_now= True)
name_site = models.CharField(max_length=200, default='NA')
url_site = models.URLField(max_length=200, default='https://exemple.fr/')
content = models.TextField()
I. I want my title (unique=False) because I have some similar titles. So is it possible to save my slug (editable=False) with slugify with something like that:
slug_str = "%s %s" % (self.title, 4 random numbers like that 0476)
If anyone has a better idea, I'm interested in
Thanks a lot :) have a good holidays and take care
Here are a couple functions that I use. You pass in the model instance and the desired title into unique_slugify which will continue trying to add random strings until one is created that doesn't already exist.
import random
import string
def random_string_generator(size=10, chars=string.ascii_lowercase + string.digits):
return ''.join(random.choice(chars) for _ in range(size))
def unique_slugify(instance, slug):
model = instance.__class__
unique_slug = slug
while model.objects.filter(slug=unique_slug).exists():
unique_slug = slug
unique_slug += random_string_generator(size=4)
return unique_slug
I usually use it by overriding the model save method.
class YourModel(models.Model):
slug = models.SlugField(max_length=200, unique=True)
title = models.CharField(max_length=200)
def save(self, *args, **kwargs):
if not self.slug:
self.slug = unique_slugify(self, slugify(self.title))
super().save(*args, **kwargs)
in my opinion, it doesn't make sens two posts may have the same title even with two different slugs, it even confuses readers and even worst it's very bad for SEO. i suggest you to avoid this path and try to review the logic
in models.py
class Post(models.Model):
title = models.CharField(_('title'), max_length=200,
unique=True, # title should be unique
help_text=_('The title of the entry.')
)
slug = models.SlugField(_('slug'), max_length=200,
unique=True, null=True, blank=True,
help_text=_(
'If blank, the slug will be generated automatically '
'from the given title.'
)
)
[..]
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.title) # here you don't need to add random numbers since the title is already unque
super().save(*args, **kwargs)
in admin.py
class PostAdmin(admin.ModelAdmin):
list_display = ('title', .. )
readonly_fields = ('slug', .. ) # Add slug to read_only fields to make it not editable if you want

Django SlugField "This field is required" error

In my django project there's a Blog model which I'm willing to create a slug for it's title:
class Blog(models.Model):
title = models.CharField(default='', max_length=100, verbose_name=u'عنوان')
slug = models.SlugField(max_length=100, allow_unicode=True)
# other stuffs
def save(self, *args, **kwargs):
self.slug = slugify(self.title, allow_unicode=True)
super(Blog, self).save(*args, **kwargs)
def __str__(self):
return self.slug
In django admin I don't fill slug field and when I hit the save button it says:
This field is required.
Isn't my code suppose to create slug automatically? Is there something else I should do?
You should set blank=True for your slug field. This way it won't be required and it will be set to slugified title when save method will run.
class Blog(models.Model):
title = models.CharField(default='', max_length=100, verbose_name=u'عنوان')
slug = models.SlugField(max_length=100, allow_unicode=True, blank=True)

Select a valid choice ModelMultipleChoiceField error

I'm trying to make form that allows to user selecting employers by the checkbox list, depends on their team using ModelMultipleChoiseField. I can filter proper employers per team but when i select any of then i had error
Select a valid choice. 3 is not one of the available choices.
And i don't know what is wrong ;/
This is my form class :
class TaskDictForm(forms.Form):
team_id = 0
order = forms.IntegerField(min_value=0)
name = forms.CharField(max_length=200)
start = forms.DateTimeField()
previous = forms.CharField(max_length=200)
employers = forms.ModelMultipleChoiceField(queryset=None, widget=forms.CheckboxSelectMultiple)
def __init__(self, *args, **kwargs):
initial = kwargs.get('initial', {})
if hasattr(self, 'initial_values') and not kwargs.get('data'):
for field_name, value in self.initial_values.items():
if not getattr(kwargs.get('instance', None), field_name, None):
initial[field_name] = value
kwargs.update({'initial': initial})
if hasattr(self, 'required_fields'):
for field_name in self.required_fields:
self.fields[field_name].required = True
tmp = kwargs.pop('pk', None)
self.team_id = tmp
super(TaskDictForm, self).__init__(*args, **kwargs)
self.fields['employers'].queryset = Employer.objects.filter(team=self.team_id)
Employer and Team models:
class Team(models.Model):
name = models.CharField(default='Team', max_length=100, unique=True)
def __str__(self):
return self.name
class Employer(models.Model):
user = models.OneToOneField(User, on_delete=models.SET_NULL, null=True)
team = models.ForeignKey(Team, on_delete=models.SET_NULL, null=True)
team_leader = models.BooleanField(default=False)
project_menager = models.BooleanField(default=False)
# project_id = models.ForeignKey(Project, on_delete=models.SET_NULL, null=True)
hourly_rate = models.IntegerField(default=0)
working_time = models.FloatField(default=0.0)
def __str__(self):
return (self.user.first_name + ' ' + self.user.last_name)
The same proble is when i try use MultipleChoiseField

How to create an inline formset for a reverse foreign key relationship

I have a Property Model as follows =
class Property(models.Model):
property_type = models.CharField(max_length=255, default='Apartment')
specifications = models.CharField(max_length=255, default='Basic')
built_up_area = models.FloatField(max_length=6, null=False, default=0)
total_area = models.FloatField(null=False, default=0)
number_of_bedrooms = models.CharField(max_length=3, default=1)
number_of_bathrooms = models.CharField(max_length=3, default=1)
number_of_parking_spaces = models.CharField(max_length=2, default=0)
address_line_one = models.CharField(max_length=255, null=False)
address_line_two = models.CharField(max_length=255, null=True, default=None)
connectivity = models.CharField(max_length=255, default=None, null=True)
neighborhood_quality = models.CharField(max_length=255, default=None,
null=True)
comments = models.CharField(max_length=255, default=None, null=True)
city = models.ForeignKey('City')
state = models.ForeignKey('State')
pin_code = models.ForeignKey('PinCode')
developer = models.ForeignKey('Developer', null=True, default=None)
owner = models.ForeignKey('Owner', null=True, default=None)
created_by = models.ForeignKey('custom_user.User')
project = models.ForeignKey('Project')
def __unicode__(self):
return self.property_type
class Meta:
verbose_name_plural = 'Properties'
And a City model as follows -
class City(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(unique=True)
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(City, self).save(*args, **kwargs)
def __unicode__(self):
return self.name
Now I want to make a single form where I can enter the Property details and while entering the city, I can enter the name of the city instead of selecting from the dropdown list.
So how do I create the inline formset using the inlineformset_factory to create the form?
==EDIT==
I've tried to use the following code to create the formset
CityFormset = inlineformset_factory(City, Property,
fields=('city',),
extra=0,
min_num=1,
can_delete=False)
You've misunderstood what an inline formset is. It's for editing the "many" side of a one-to-many relationship: that is, given a parent model of City, you could edit inline the various Properties that belong to that city.
You don't want a formset at all to simply edit the single City that a property can belong to. Instead, override the city field within your Property form to be a TextField, and either create a new City or find an existing one in the clean_city method.
class PropertyForm(forms.ModelForm):
city = forms.TextField(required=True)
class Meta:
model = Property
exclude = ('city',)
def __init__(self, *args, **kwargs):
super(PropertyForm, self).__init__(*args, **kwargs)
if self.instance and not self.data:
self.initial['city'] = self.instance.city.name
def save(self, commit=True):
city_name = self.cleaned_data['city']
city, _ = City.objects.get_or_create(name=city_name)
instance = self.save(commit=False)
instance.city = city
if commit = True:
instance.save()
return instance