I have a model defined and over 100+ entries of data in my DB. I would like to auto populate a slug field and see it show up in the admin, since adding new entries for 100+ fields is not something I would like to do.
AutoSlug() field doesnt seem to be working when I add it to my model and make the migrations, prepopulated_fields = {'slug': ('brand_name',)} does not work using it within my admin.py and as well I have tried to add the default field on the slug as my desired field name within the Model but to no avail the solution didnt work.
Is their any other suggestions on how to get the slug filed pre-populated?
class Brand(models.Model):
brand_name = models.CharField(unique=True, max_length=100, blank=True, default="", verbose_name=_('Brand Name'))
slug = models.SlugField(max_length=255, verbose_name=_('Brand Slug'), default=brand_name)
You can try adding a save method to the Brand class.
from django.utils.text import slugify
class Brand(models.Model):
...
def save(self, *args, **kwargs):
self.slug = slugify(self.brand_name)
super(Brand, self).save(*args, **kwargs)
then run:
python manage.py shell
>>>from app.models import Brand
>>>brands = Brands.objects.all()
>>>for brand in brands:
>>> brand.save()
Also, I would change brand_name var to just name.
I think it's better to add a pre_save signal on the Brand model.
from django.dispatch import receiver
from django.db.models import signals
from django.contrib.auth import get_user_model
from django.utils.text import slugify
#receiver(signals.pre_save, sender=<model name>)
def populate_slug(sender, instance, **kwargs):
instance.slug = slugify(instance.brand_name)```
I think I have an idea, which would do the job, however I am not sure if that would be the best way to do it.
I would use slugify function. I would create the view which - after it was called - would get all model's objects, iterate over them and populate each model's slug field using django.utils.text.slugify function with model's brand_name as a value.
Related
Well, I have a VagasUsuarios model and a Questionario model. I would like that when I updated the Questionario.pontuacao_questionario field via django admin, my other VagaUsuarios.pontuacao_vaga field would be updated as well. Is there a way to do this?
thanks for listening =)
My Models:
class Questionario(models.Model):
usuario = models.ForeignKey(Contas, on_delete=models.CASCADE)
[...]
pontuacao_questionario = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True,verbose_name="Pontuacao do QuestionĂ¡rio")
class VagasUsuarios(models.Model):
usuario = models.ForeignKey(Contas, on_delete=models.CASCADE)
[...]
pontuacao_vaga = models.DecimalField(max_digits=5, decimal_places=2, verbose_name="Pontuacao da Vaga")
You could do this with signals.
Example:
from django.db.models.signals import post_save
from django.dispatch import receiver
from myapp.models import Questionario, VagasUsuarios
#receiver(post_save, sender=Questionario)
def my_handler(sender, instance, **kwargs):
obj = VagasUsuarios.objects.get(...)
obj.pontuacao_vaga = instance.pontuacao_questionario
obj.save()
Another is to override the save() (or rather clean()) method of your model and when it gets updated to fetch all the relevant VagasUsuarios-objects you want to update and update them.
Note on clean(): You got to call the clean-method yourself unless you are using the Django admin.
I am a Django beginner and I am trying to make read-only a 'price' field for an order. I think, based on what I have understood, this cannot be done inside the model itself, but rather inside a form.
Since I am using a CreateView generic view, I thought this could have been done by setting the attribute disabled equal to True, as said here.
so what I have done is, in views.py
from django.shortcuts import render
from django.views.generic import CreateView
from .models import Order
from django import forms
# Create your views here.
class CreateOrderView(CreateView):
model = Order
template_name = 'home.html'
meal_price = forms.DecimalField(disabled=True)
fields = [
'meal_name',
'meal_price',
'restaurant',
'customer',
]
But this doesn't work.
Here is my models.py
from django.db import models
from restaurant.models import Restaurant
from account.models import Customer
# Create your models here.
class Order(models.Model):
meal_name = models.CharField(max_length=255)
meal_price = models.DecimalField(max_digits=5, decimal_places=2)
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE, default=None)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, default=None)
Can anybody give me a hint?
Please consider that I am still learning so I would prefer coded answers to descriptive ones.
Thank you in advance
Ok, thanks to dirkgroten, I have worked out the answer.
Basically what is needed (in my case) is:
an Order model in models.py
from django.db import models
from restaurant.models import Restaurant
from account.models import Customer
# Create your models here.
class Order(models.Model):
meal_name = models.CharField(max_length=255)
meal_price = models.DecimalField(max_digits=5, decimal_places=2)
restaurant = models.ForeignKey(Restaurant, on_delete=models.CASCADE, default=None)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, default=None)
an OrderForm(ModelForm) in forms.py that modifies the price field setting the disabled attribute to true
from django.forms import ModelForm
from .models import Order
from django import forms
class OrderForm(ModelForm):
meal_price = forms.DecimalField(max_digits=5, decimal_places=2, disabled=True)
class Meta:
model = Order
fields = [
'meal_name',
'meal_price',
'restaurant',
'customer',
]
an OrderView(CreateView) in views.py
from django.shortcuts import render
from django.views.generic import CreateView
from .forms import OrderForm
# Create your views here.
class OrderView(CreateView):
form_class = OrderForm
template_name = 'home.html'
I have no experience with Django's CreateView but from what I read it works similar to a separate form. You could try something like this:
class CreateOrderView(CreateView):
model = Order
template_name = 'home.html'
fields = [
'meal_name',
'meal_price',
'restaurant',
'customer',
]
def __init__(self, *args, **kwargs):
super(CreateOrderView, self).__init__(*args, **kwargs)
self.fields['meal_price'].widget.attrs['disabled'] = True
From my experience, the disabled attribute will be good for security reasons as far as protecting against the user editing the HTML and changing the value. However you won't be able to access this value when passed into a clean method. If you need to perform actions on the value you should change 'disabled' to 'readonly', but you won't have the same data protection that disabled offers.
I would just like to create a model that essentially prevents the same date being selected by two different users (or the same user).
E.g if User1 has selected 2019-01-10 as a "date" for a booking, then User2 (or any other Users) are not able to create an object with that same date.
I have created a very basic model that can allow different Users to create an object using the DateField(). Using the Django admin page, I can create different instances of objects by two different Users (admin and Test_User).
In order to try to ensure that a new object can't be created if that date has already been used by a different object I have tried the following approach:
a compare function that utilizes __dict__.
models.py
from __future__ import unicode_literals
from django.db import models, IntegrityError
from django.db.models import Q
from django.contrib.auth.models import User
from datetime import datetime
class Booking(models.Model):
date = models.DateField(null=False, blank=False)
booked_at = models.DateTimeField(auto_now_add=True)
booking_last_modified = models.DateTimeField(auto_now=True)
class PersonalBooking(Booking):
user = models.ForeignKey(User, on_delete=models.CASCADE)
def compare(self, obj):
excluded_keys = 'booked_at', '_state', 'booking_last_modified', 'user',
return self._compare(self, obj, excluded_keys)
def _compare(self, obj1, obj2, excluded_keys):
d1, d2 = obj1.__dict__, obj2.__dict__
for k,v in d1.items():
if k in excluded_keys:
continue
try:
if v != d2[k]:
pass
except IntegrityError as error:
print(error)
print('Date already selected by different User. Please select another date.')
admin.py
from django.contrib import admin
from . import models
from .models import Booking, PersonalBooking
class PersonalBookingAdmin(admin.ModelAdmin):
list_display = ('format_date', 'user', )
def format_date(self, obj):
return obj.date.strftime('%d-%b-%Y')
format_date.admin_order_field = 'date'
format_date.short_description = 'Date'
def user(self, obj):
return obj.user()
user.admin_order_field = 'user'
user.short_description = 'User'
admin.site.register(models.PersonalBooking, PersonalBookingAdmin)
It didn't work as I had hoped, objects with the same date could still be created by the same or different users. Perhaps there is a simpler way? Or maybe I need to use the Q() class? I am not very familiar with it.
Any help is greatly appreciated.
Thanks.
You could do this validation at the database level by setting the unique attribute to True in your model's field.
class Booking(models.Model):
date = models.DateField(null=False, blank=False, unique=True)
booked_at = models.DateTimeField(auto_now_add=True)
booking_last_modified = models.DateTimeField(auto_now=True)
But this would present issues if the field was changed later to store time.
If you are going to be storing the time as well, you could override the model's default save function to check that there isn't another Booking with the same date (__date) each time it is saved. exists() returns True if there is a match, so this will throw a ValidationError if there is a match.
from django.core.exceptions import ValidationError
class Booking(models.Model):
date = models.DateTimeField(null=False, blank=False)
booked_at = models.DateTimeField(auto_now_add=True)
booking_last_modified = models.DateTimeField(auto_now=True)
def save(self, *args, **kwargs):
# Make sure there are no bookings on the same day
if Booking.objects.exclude(pk=self.pk).filter(date__date=self.date.date).exists():
raise ValidationError('There cannot be two bookings with the same date.')
super(Booking, self).save(*args, **kwargs)
Try this
https://docs.djangoproject.com/en/2.1/ref/models/fields/#unique-for-date
For user column set unique_for_date=True
I'm trying to limit the maximum amount of choices a model record can have in a ManyToManyField.
In this example there is a BlogSite that can be related to Regions. In this example I want to limit the BlogSite to only be able to have 3 regions.
This seems like something that would have been asked/answered before, but after a couple hours of poking around I haven't been able to find anything close. For this project, I'm using Django 1.3.
#models.py
class BlogSite(models.Model):
blog_owner = models.ForeignKey(User)
site_name = models.CharField(max_length=300)
region = models.ManyToManyField('Region', blank=True, null=True)
....
class Region(models.Model):
value = models.CharField(max_length=50)
display_value = models.CharField(max_length=60)
....
Any ideas?
You can override clean method on your BlogSite model
from django.core.exceptions import ValidationError
class BlogSite(models.Model):
blog_owner = models.ForeignKey(User)
site_name = models.CharField(max_length=300)
regions = models.ManyToManyField('Region', blank=True, null=True)
def clean(self, *args, **kwargs):
if self.regions.count() > 3:
raise ValidationError("You can't assign more than three regions")
super(BlogSite, self).clean(*args, **kwargs)
#This will not work cause m2m fields are saved after the model is saved
And if you use django's ModelForm then this error will appear in form's non_field_errors.
EDIT:
M2m fields are saved after the model is saved, so the code above will not work, the correct way you can use m2m_changed signal:
from django.db.models.signals import m2m_changed
from django.core.exceptions import ValidationError
def regions_changed(sender, **kwargs):
if kwargs['instance'].regions.count() > 3:
raise ValidationError("You can't assign more than three regions")
m2m_changed.connect(regions_changed, sender=BlogSite.regions.through)
Give it a try it worked for me.
Working! I have used this and its working properly.
Validation required before saving the data. So you can use code in form
class BlogSiteForm(forms.ModelForm):
def clean_regions(self):
regions = self.cleaned_data['regions']
if len(regions) > 3:
raise forms.ValidationError('You can add maximum 3 regions')
return regions
class Meta:
model = BlogSite
fields = '__all__'
Hi I need really very very simple example. First my models:
#This my student models
from django.db import models
SEX_CHOICES= (
('M', 'Male'),
('F', 'Female'),
)
class Students(models.Model):
student_name = models.CharField(max_length=50)
student_sex = models.CharField(max_length=8, choices=SEX_CHOICES)
student_city = models.Charfield(max_length=50)
student_bio = models.TextField()
def __unicode__(self):
return self.student_name
O.K. Let see my ClassRooms Model.
#This my ClassRooms models
from django.db import models
from myproject.students.models import *
class ClassRooms(models.Model):
class_number= models.CharField(max_length=50)
class_student_cities = models.ForeignKey(Students)
class_year = models.DateField()
def __unicode__(self):
return self.class_number
How can i show in the class_student_cities area the Students.student_city datas? I guess that about django-admin area. When i do it withclass_student_cities = models.ForeignKey(Students) i just see in that area the Students.student_name data (ex: John Smith). I want to see JUST Students.student_cities data (ex: NewYork). Can you give me a little example?
Should i use something like that:
class_student_cities = models.ForeignKey(Students.student_cities)
Many Thanks!
Try redifinition unicode method.
def __unicode__(self):
return self.student_city
So you'll see in the field student city.
Well, I tried to remake your application to set data with forms class. Something like this in admin.py in your application:
from django.contrib import admin
from django import forms
from myapp.models import *
class ClassRoomsAdminForm(forms.ModelForm):
class Meta:
model = ClassRoom
def __init__(self, *arg, **kwargs):
super(ClassRoomsAdminForm, self).__init__(*arg, **kwargs)
self.fields[' class_student_cities'].choices = [(csc.id,csc.student_city) for csc in Students.objects.all()
class ClassRoomsAdmin(admin.ModelAdmin):
form = ClassRoomsAdminForm
admin.site.register(ClassRooms,ClassRoomsAdmin)
Maybe you'll need to fix something, but I hope it will work. You will set init function to your forms, so in admin panel you set all choices to everything you keep in your Students model. csc.id you'll need to make this object iterable (cities aren't unique) and then you can choose everything from Students model to set in the field.