Django override save method with changing field value - python

I need some help with overriding save method with changing field value.
I have such structure:
models.py
class Category(models.Model):
name = models.CharField(max_length=255, validators=[MinLengthValidator(3)])
parent = models.ForeignKey('self', blank=True, null=True,
related_name='children',
on_delete=models.CASCADE
)
class Product(models.Model):
name = models.CharField(max_length=255, validators=[MinLengthValidator(3)])
to_category = models.ForeignKey(Category, on_delete=models.SET_NULL,
blank=True, null=True,
)
to_categories = models.ManyToManyField(Category, blank=True,
related_name='categories',
)
def save(self, *args, **kwargs):
super(Product, self).save(*args, **kwargs)
So I can't find a correct sollution for save method. I can select category on the "to_category" and categories on "to_categories" field, but I need if I selected one of the categories on the "to_category" field then save the Product, this selected field must be automatically selected on the "to_categories" field.

I found a sollution.
admin.py:
class ProductAdmin(admin.ModelAdmin):
...
def save_related(self, request, form, formsets, change):
super(ProductAdmin, self).save_related(request, form, formsets, change)
category = Category.objects.get(id=form.instance.to_category.id)
form.instance.to_categories.add(category)

Related

Django unique slug field for two or more models

I have such structure:
class Category(models.Model):
name = models.CharField(max_length=255, validators=[MinLengthValidator(3)])
parent = models.ForeignKey('self', blank=True, null=True,
related_name='children',
on_delete=models.CASCADE
)
slug = models.SlugField(max_length=255, null=False, unique=True)
class Product(models.Model):
name = models.CharField(max_length=255, validators=[MinLengthValidator(3)])
to_category = models.ForeignKey(Category, on_delete=models.SET_NULL,
blank=True, null=True,
)
slug = models.SlugField(max_length=255, null=False, unique=True)
I have created one category with slug "test". When I try to create new category with slug "test" I got warning message and it is Ok. But If I try to create product with slug "test" I dont have warning and this is not good in my case. Is there a solution or method to validate slug field for uniqueness with Product and Category model?
You can override the save method for each, and then check if the given slug already exists for a product or category.
def is_slug_unique(slug):
product_exists = Product.objects.filter(slug=slug).exists()
category_exists = Category.objects.filter(slug=slug).exists()
if product_exists or category_exists:
return False
else:
return True
class Category(models.Model)
...
def save(self, *args, **kwargs):
slug_unique = is_slug_unique(self.slug)
if not slug_unique:
# do something when the slug is not unique
else:
# do something when the slug is unique
super().save(*args, **kwargs)
class Product(models.Model)
...
def save(self, *args, **kwargs):
slug_unique = is_slug_unique(self.slug)
if not slug_unique:
# do something when the slug is not unique
else:
# do something when the slug is unique
super().save(*args, **kwargs)
An idea might be to create a Slug model that stores all the slugs, optionally with a backreference to the object:
class Slug(models.Model):
slug = models.SlugField(max_length=255, primary_key=True)
Then the slugs in your models are ForeignKeys to that Slug model, and you check if such slug already exists:
from django.core.exceptions import ValidationError
class Product(models.Model):
name = models.CharField(max_length=255, validators=[MinLengthValidator(3)])
to_category = models.ForeignKey(
Category, on_delete=models.SET_NULL, blank=True, null=True
)
slug = models.ForeignKey(Slug, on_delete=models.PROTECT)
def validate_slug(self):
if self.pk is not None and Slug.objects.filter(pk=self.slug_id).exclude(
product__pk=self.pk
):
raise ValidationError('The slug is already used.')
def clean(self, *args, **kwargs):
self.validate_slug()
return super().clean(*args, **kwargs)
def save(self, *args, **kwargs):
self.validate_slug()
return super().save(*args, **kwargs)
That being said, often overlapping slugs for different entity types are allowed.

RESOLVED: Django Admin: How to display currently logged in user's ID on the display list?

I am very new to Django and trying to make my first project. I find it difficult to display currently logged in user's ID on the list in Django admin model.
I found a solution of how to get the appriopriate ID thanks to Shang Wang (Django admin: Get logged In users id in django)
But I don't know how to display this ID on the list, right next to Patient ID, Unit and Create time (mentioned in the code below).
My admin.py looks like this :
class GeneralAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
obj.user = request.user
super(GeneralAdmin, self).save_model(request, form, obj, change)
list_display = ('id_patient', 'unit', 'create_time')
search_fields = ('id_patient', 'unit', 'create_time')
readonly_fields = ('unit', 'Birth_date','create_time',)
Edit 2: my models.py file:
from django.db import models
from datetime import date, datetime
from django.contrib.auth.models import User
class Unit(models.Model):
id = models.AutoField(primary_key=True)
description = models.CharField(max_length=300)
class Patient(models.Model):
id = models.AutoField(primary_key=True, verbose_name='Patient ID')
unit = models.ForeignKey(Unit, on_delete=models.CASCADE)
Birth_date = models.DateField()
introducer = models.ForeignKey(User,
help_text = "This field will be calculated automatically",
blank=True, null=True, on_delete=models.CASCADE)
#here I try to get current user's ID
def save(self, *args, **kwargs):
self.introducer = User.username #here I get an error
super().save(*args, **kwargs)
class General(models.Model):
id = models.AutoField(primary_key=True)
id_patient = models.ForeignKey(Patient, on_delete=models.CASCADE)
unit = models.ForeignKey(Unit, help_text = "This field will be calculated automatically", blank=True, null=True, on_delete=models.CASCADE)
Birth_date = models.DateField(blank=True, null=True, help_text = "This field will be calculated automatically")
create_time = models.DateTimeField(blank=True, null = True, default = datetime.now())
def save(self, *args, **kwargs):
self.Birth_date = self.id_patient.Birth_date
self.unit = self.id_patient.unit
super().save(*args, **kwargs)
class Primary_Treatment_EBRT(models.Model):
id = models.AutoField(primary_key=True)
unit = models.ForeignKey(Unit, help_text = "This field will be calculated automatically", blank=True, null=True, on_delete=models.CASCADE)
Birth_date = models.DateField(blank=True, null=True, help_text = "This field will be calculated automatically")
create_time = models.DateTimeField(blank=True, null = True, default = datetime.now())
id_patient = models.ForeignKey('Patient', on_delete=models.CASCADE)
def save(self,*args, **kwargs):
self.Birth_date = self.id_patient.Birth_date
self.unit = self.id_patient.unit
super().save(*args, **kwargs)
How to put obj.user's ID into list_display to display it on the list like id_patient, unit, or create_time?
Problem RESOLVED:
My admin.py change:
class PatientAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change, *args, **kwargs):
obj.user = request.user
obj.create_time = datetime.now()
super(PatientAdmin, self).save_model(request, obj, form, change, *args, **kwargs)
My models.py change:
create_time = models.DateTimeField(blank=True, null = True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True,
null=True, on_delete=models.CASCADE)

Filter foreign key data entered by a user in a Django form

I have this Model that has a foreign Key. I want anytime a user if filling the form, Only the data entered by the user in the foreign key model should be shown to him as a dropdown.
Model.py
class Nomination(models.Model):
fullname = models.CharField(max_length=120)
nominee_id = models.CharField(max_length=100, default=increment_invoice_number, null=True, blank=True)
category = models.ForeignKey(Category, on_delete=models.CASCADE)
image = models.ImageField(upload_to='nominations_images')
slug = models.SlugField(max_length=150, blank=True)
votes = models.IntegerField(default=0, blank=True)
date = models.DateTimeField(auto_now_add=True)
createdby = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True)
forms.py
class CategoryForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(CategoryForm, self).__init__(*args, **kwargs)
# access object through self.instance...
self.fields['createdby'].queryset = Award.objects.filter(createdby=self.instance.user)
class Meta:
model = Category
fields = "__all__"
This is the error I get.
'Nomination' object has no attribute 'user'
I don't understand how you got this error, because you have form for Category model and self.instanse should contain Category object, not Nomination. Anyway, if you have to use current user for form filtering, you can get it as form argument.
forms.py
class MyFilteredForm(ModelForm):
def __init__(self, user, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['createdby'].queryset=Award.objects.filter(createdby=user)
...
views.py
class SomeView(View):
def get(self, request, *args, **kwargs):
form = MyFilteredForm(request.user)
return render(request, 'form.html', {'form': form})
It can contains mistakes, but i think you get my idea.

How to auto add a field in django admin model calculating age

is it possible to add an age field that is auto filled in the runtime based on another date of birth field at the django admin interface, i added a screenshot trying to explain more what i mean
my models.py
class FamilyMember(models.Model):
transaction = models.ForeignKey(Transaction, on_delete=models.CASCADE)
family_group = models.ForeignKey(FamilyGroup,
on_delete=models.CASCADE,
null=True,
blank=True)
name = models.CharField(max_length=100, null=True, blank=True)
date_of_birth = models.DateField(null=True, blank=True)
relationship = models.ForeignKey(Relationship, on_delete=models.PROTECT)
dependant_child_age_range = models.ForeignKey(DependantChildAgeRange,
null=True,
blank=True,
on_delete=models.PROTECT)
care_percentage = models.PositiveSmallIntegerField(
null=True, blank=True, validators=[
MaxValueValidator(100),
])
income = models.DecimalField(max_digits=6,
decimal_places=2,
null=True,
blank=True)
rent_percentage = models.PositiveSmallIntegerField(
null=True, blank=True, validators=[
MaxValueValidator(100),
])
admin.py
class FamilyMemberInline(admin.TabularInline):
def formfield_for_foreignkey(self, db_field, request, **kwargs):
action = request.META['PATH_INFO'].strip('/').split('/')[-1]
if action == 'change':
transaction_id = request.META['PATH_INFO'].strip('/').split('/')[-2]
if db_field.name == "family_group":
kwargs["queryset"] = FamilyGroup.objects.filter(transaction=transaction_id)
return super(FamilyMemberInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
model = FamilyMember
extra = 0
def sufficient_info_provided (self, obj):
return obj.sufficient_information_provided
sufficient_info_provided.boolean = True
readonly_fields = ['sufficient_info_provided',]
Override your inline's get_queryset method to annotate the queryset with the calculation. The annotation will add an age attribute to each object in the queryset.
Then as you can see in the ModelAdmin.list_display documentation, you can include a string representing a ModelAdmin method that accepts one argument, the model instance. Inline's work in the same way but you must include the method in ModelAdmin.readonly_fields.
Putting it all together:
class FamilyMemberInline(admin.TabularInline):
...
fields = (..., 'get_age')
readonly_fields = ('get_age',)
def get_queryset(self, request):
return (
super().get_queryset(request)
.annotate(age=...)
)
def get_age(self, instance):
return instance.age

Specific user content Add, Edit, Delete particular data for particular user

I'm starting to organize a project with a few models, but the two main models in question are Brands and Products.
I will allow clients to login on Django Admin site to edit their Brands and to Add, Edit and Delete their own Products.
However when editing their Products, said client will only be able to Add, Edit & Delete their own Products; so for example their Brand Name will already be chosen in the Foreign Key dropdown, due to the permission constrain. From there they can repeat the process of adding their Brands and Delete and Edit the Brands as they Choose.
Current Setup & Problem
Currently I have this setup for Brands the way I would like, just for a client to see only their Brands to Edit. But for Products, they can see all the products, even for different Brands (which is not what I want); and when they create a new product they have to choose from a dropdown of products to match with (which is not what I want). I am using the default Django permissions as of now, with some ModelAdmin methods.
models.py
class TimeStampedModel(models.Model):
"""
An abstract base class model that provides self updating
``created`` and ``modified`` fields.
"""
created = models.DateTimeField(auto_now_add=True, blank=True, null=True)
modified = models.DateTimeField(auto_now=True, blank=True, null=True)
# Metadata
class Meta:
abstract = True
class Brand(TimeStampedModel):
"""
Information for each brand
"""
# Primary Key
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
# General information
brand_name = models.CharField(max_length=100, blank=True, default="", unique=True)
brand_description = models.TextField(max_length=100, blank=True, default="")
brand_origin_city = models.CharField(max_length=100, blank=True, default="")
brand_origin_state_or_country = models.CharField(max_length=100, blank=True, default="",
help_text=_('State (USA only), Country (International only)'), verbose_name=_('State or Country'))
brand_feature_image = CloudinaryField('Featured Brand Image', null=True, blank=True)
brand_isActive = models.BooleanField(default=False, verbose_name=_('Brand active'))
# Metadata
class Meta:
verbose_name = _('Brand')
verbose_name_plural = _('Brands')
def __unicode__(self):
return "{0}".format( self.brand_name )
class Product(TimeStampedModel):
"""
Product for each brand
"""
product_name = models.CharField(max_length=100, blank=True, default="", verbose_name=_('Product Name') )
product_url = models.URLField(max_length=100, blank=True, default="",
help_text=_('This is for the product web url to the particular item on your website.'), verbose_name=_('Product Url'))
product_price = CurrencyField( verbose_name=_('Product price') )
product_image = CloudinaryField('Product Image', blank=True, null=True)
# Foreign Key
brand = models.ForeignKey('Brand', null=True, to_field="id")
#Metadata
class Meta:
verbose_name = _('Product')
verbose_name_plural = _('Products')
def __unicode__(self):
return "{0}".format( self.product_name )
Admin.py
class BrandAdmin(admin.ModelAdmin):
list_display = ["brand_name", "brand_origin_city", "brand_origin_state_or_country"]
search_fields = ["brand_name"]
list_per_page = 10
def save_model(self, request, obj, form, change):
obj.user = request.user
super(BrandAdmin, self).save_model(request, obj, form, change)
obj.save()
def get_queryset(self, request):
qs = super(BrandAdmin, self).get_queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(brand_name=request.user)
class ProductAdmin(admin.ModelAdmin):
list_display = ["product_name","product_price"]
search_fields = ["product_name"]
list_per_page = 20
def save_model(self, request, obj, form, change):
obj.user = request.user
super(ProductAdmin, self).save_model(request, obj, form, change)
obj.save()
def get_queryset(self, request):
qs = super(ProductAdmin, self).get_queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(brand=request.user)
# Register Models below
admin.site.register(Brand, BrandAdmin)
admin.site.register(Product, ProductAdmin)
What would be a solution to solve this issue?
class ProductAdmin(admin.ModelAdmin):
...
def get_queryset(self, request):
...
return qs.filter(brand__brand_name = request.user)
Hope this work. Let me know if not.

Categories

Resources