Django prepopulated_fields removes stop words from slugs? - python

I am trying to automatically generate slugs to use in URLs of a Django app.
So far I have achieved this using prepopulated_fields in admin.py.
However, slugs generated with prepopulated_fields do not include stop words (i.e., the string "I love to code" has slug "love-code").
Is there a way to automatically generate slugs that also include stop words?

Slug is generated on fronted using prepopulate.js function and does not have any configuration options, you could add your custom javascript instead
Or
forget about preopopulated_fields and instead override save method on Headword model and generate slug using slugify() something in a line of
from django.utils.text import slugify
class Headword(models.Model):
...
def save(self, *args, **kwargs):
self.slug = slugify(self.headword, allow_unicode=True)
super().save(*args, **kwargs)

Related

How can create model that couldn't be changed by admin?

I need to create Django model that couldn't by admin, but he should be avialable to see it.
It's content will be input from site.
How can I do it?
Basically if you want to disable the editability of the model you may want to make use of Django Admin's permission framework, like this:
class PersonAdmin
def has_change_permission(self, request, obj=None):
# only allows superusers to edit
return request.user.is_superuser
You may also want to try the readonly_field like:
class PersonAdmin
readonly_fields = ('name','sex',)
But the problem of doing this is that you will see the save buttons on the editing page despite that nothing is allowed to be changed, which is probably not what you want
Designate that it should be read only
In your Model Admin, you can specify that certain fields are not to be changed.
class ProfileAdmin(admin.ModelAdmin):
readonly_fields = ('source', 'campaign')
Just put that in your admin.py and then when you got to register your site, use this:
admin.site.register(Profile, ProfileAdmin)
instead of what you are probably currently using, which would be
admin.site.register(Profile)
You can list the fields you want on both fields and readonly_fields.
class AuthorAdmin(admin.ModelAdmin):
fields = ('name', 'title')
readonly_fields = ('name', 'title')

Django override slugify globally

I've encountered a problem with Django's built-in slugify function. I'm building a website using Django framework. The site must have a forum app. After a bit of searching, I've found one. It works great, however, it's using the slugify function heavily on the topic titles to create "human readable" links to its pages. The problem is, we are writing in Russian, so as the result, it generates non-ASCII URLs which look like an unreadable mess of unicode data when trying to copy the link from the browser (and also throws an exception when trying to log them).
Is there a way to override the Django's django.utils.text.slugify globally for the whole project so I don't need to include half of the third party library only to change the import statements in their models.py ?
One way where it is not global is to write your own slugify function and then you can utilise that by calling it in overridden save method of the model where you want to slugify the title/name field.
Eg.
class Post(models.Model):
title = models.CharField(max_length=512)
slug = models.CharField(max_length=1024)
def save(self, *args, **kwargs):
your_slugify_function(self, self.title)
super(Post, self).save(*args, **kwargs)
I think you can define a abstract model class with overriding the save method with slugify function. Like this:
class AbstractBase(models.Model):
slug = models.SlugField()
class Meta:
abstract = True
def save(self, *args, **kwargs):
self.slug = slugify.Slugify(self.slug)
return super(AbstractBase, self).save(*args, **kwargs)
And sub-class your rest of the models from this abstract class like:
class Post(AbstractBase):
# rest of the post fields
In this way, slugify will be done only in one place and work globally across all models.

Cannot modify a model field on save()

I'm a beginner with Django, and first time askig :)
I'm following a simple tutorial on generating a slug for a string (let's say a slug for a blog post generated from its title).
Perhaps I'm following an outdated guide, perhaps I'm missing a basic thing, I have no idea.
Django 2.0
Python 3.6
I am trying to do a very simple task of slugifying a simple string, so:
User enters a string in a simple form
When hitting 'save', the title goes through slugify and creates the
slug
Save.
models.py
from django.db import models
class Testmodel(models.Model):
title = models.CharField(max_length=220)
slug = models.SlugField(unique=True, null=True)
def __str__(self):
return self.title
views.py
from django.views.generic.edit import CreateView
class TestCreate(CreateView):
model = Testmodel
fields = '__all__'
forms.py
from django.forms import ModelForm
from .models import Testmodel
class TestCreateForm(ModelForm):
class Meta:
model = Testmodel
fields = '__all__'
Up until here everythig works, if I enter the slug manualy. In order to do it automaticaly, I have tried:
Overriding the save() method withing my ModelForm class.
Overriding the form_valid() method within the CreateView
Overriding the save() method within the model itself.
Tried to connect a pre_save signal to the model.
In all of these 4 tries, I had the same results:
When generating the form with the slug field, I couldn't do anything because it was required.
When generating the form without the slug field, nothing happens when I hit save.
The only way I have found to dodge this issue is to set the slug field to blank = True as well. I am not sure how secure it is, though?
Thank you!
Welcome to StackOverflow. You've written a wonderfuly constructed question (Cheers!)
When generating the form with the slug field, I couldn't do anything because it was required.
Okay so first we exlcude the slug because we want it to be autogenerated.
You can do this by
class TestCreateForm(ModelForm):
class Meta:
model = Testmodel
exclude = ['slug']
Now you'll get a form without the slug field.
When generating the form without the slug field, nothing happens when I hit save.
Now we override the save() function of the model itself since slug is a part of the model.
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super().save(*args, **kwargs)
But this will generate the slug everytime the model is saved.
We can go a step further and make sure the slug is set only if the model is 'created' and not every time it is 'updated'
def save(self, *args, **kwargs):
if not self.id:
self.slug = slugify(self.title)
super().save(*args, **kwargs)

using foreign language in django slug field is not working

this question might get a bit large,i will try to explain perrty much everything whats going on.below is my heading model which fills the slug field itself by whatever is the title:
class Heading(models.Model):
category = models.ForeignKey(Category)
title = models.CharField(max_length=5000)
content =RichTextUploadingField ()
image= models.ImageField(null=True,blank=True)
date = models.DateField(default=datetime.now())
time = models.TimeField(default=datetime.now())
slug = models.SlugField(unique=True, null=True, blank=True)
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super(Heading, self).save(*args, **kwargs)
my title is in a foreign language(nepali language to be specific)
below is the image of my admin panel to fill up the heading class
as you can see my title is in foreign language but my slug field is filled automatically by converting that title in english language which is not what i want,i want my slug field to be filled in the same language as my title field.i did some search and a module called unidecode might be the solution to it,i tried using it too but since its documentation is not well i couldn't get hook of it.so if there is any other solution or how to make proper use of unidecode?any kind of help or clue would be greatly appreciated
The problem is, slugification happens in JavaScript (at least in the standard Django admin), before it reaches the server (you can look up urlify.js in the admin contrib package).
There's a new option on SlugField called allow_unicode, which might do what you want, but it's been introduced in Django 1.9.
If you cannot upgrade to 1.9 yet, you could in theory set up some endpoint on your server that would take a string, run unidecode on it, and return it, and then cook up some custom JavaScript code that would override the default slugification in the admin, but that sounds like quite a lot of work.
Another option would be to hide the slug field from the admin altogether, and do something similar to that snippet of code you posted in your question, except you should probably do this in the ModelAdmin class instead of the model itself (and you probably want to use unidecode there before passing the string to slugify).
Firstly you have to import "SLUGIFY" [from django.utils.text import slugify].
Secondly, In model,
slug = models.SlugField(allow_unicode=True, unique=True, null=True,
blank=True)
After that:
def save(self, *args, **kwargs):
 self.slug = slugify(self.title)
 if not self.slug:
  slug_str = f"{self.title}"
  self.slug = slugify(slug_str, allow_unicode=True)
 super(Blog, self).save(*args, **kwargs)
Finally Don't forget to add
[ALLOW_UNICODE_SLUGS = True]
in settings.py.

Django - Slugs - Key (slug)=() is duplicated

just started fooling around with Django and came across a link here on how to create slugs. I was told to perform the following changes to an existing model:
from django.template.defaultfilters import slugify
class Category(models.Model):
name = models.CharField(max_length=128, unique=True)
views = models.IntegerField(default=0)
likes = models.IntegerField(default=0)
slug = models.SlugField(unique=True)
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Category, self).save(*args, **kwargs)
def __unicode__(self):
return self.name
This worked out pretty well until I tried to migrate the database using:
python manage.py makemigrations
The above asked for a default value so following the guide, I gave it ''. Then:
python manage.py migrate
The above returned "DETAIL: Key (slug)=() is duplicated."
I'm not entirely sure why this happened. Perhaps it's because I'm adding a new field that is unique and I can't populate it with ''? If so, what do I have to do in order to populate the database?
The documentation explains how to migrate in these circumstances. A quick summary:
create the field without unique=True
create a migration with a RunPython function that iterates through all Categories and calls save on them, which will populate the slug
create a final migration which sets unique=True.
Actually i found an easier way to override the admin filter lists, that fixed my error of duplicated fields, So you can filter the fields in the admin panel to get all the duplicated fields and rename them ... and then migrate .. and it will work after removing all the duplication..
first create custom_filter.py file and include this code in it(assuming that you have a field with name slug)
from django.contrib.admin import SimpleListFilter
class DuplicatSlugFilter(SimpleListFilter):
"""
This filter is being used in django admin panel.
"""
title = "Duplicates"
parameter_name = "slug"
def lookups(self, request, model_admin):
return (("duplicates", "Duplicates"),)
def queryset(self, request, queryset):
if not self.value():
return queryset
if self.value().lower() == "duplicates":
return queryset.filter().exclude(
id__in=[slug.id for slug in queryset.distinct("slug").order_by("slug")]
)
Then add those lines to the admin.py file:
from .custom_filter import DuplicatSlugFilter
class CategoryAdmin(admin.ModelAdmin):
list_filter = (DuplicatSlugFilter,)
admin.site.register(Category, CategoryAdmin)
Good Luck

Categories