Override Django Unique_Together Validation by adding extra Field - django

I need some help in saving some records to the DB. Work for a parking app and i want to extend the way how unique_together queries the DB, by having an extra param. Just to understand what my situation is.
E.g i give the user possibility of booking a plot on (parking_on) 25th from 9-18 the location P1. Because the way both of the filters are uniques to the DB, someone else can't book the same plot P1 on the same dat 25th but from 19-24 ==>therefore i need some kind of validation here or manually change the way how the unique_together validation works.
Can someone please help?
Please find below my models and admin
from django.db import models
from django.conf import settings
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
from datetime import datetime, timedelta, time
from django.core.exceptions import NON_FIELD_ERRORS
today = datetime.now().date()
tomorrow = today + timedelta(1)
now = datetime.now()
l = now.hour
m = int(now.strftime("%H"))
class ParcareManager(models.Manager):
def active(self, *args, **kwargs):
return super(ParcareManager, self).filter(draft=False).filter(parking_on__lte=datetime.now())
class Parcare(models.Model):
PARKING_PLOT = (('P1', 'Parking #1'),
('P2', 'Parking #2'), ('P3', 'Parking #3'))
user = models.ForeignKey(settings.AUTH_USER_MODEL,
blank=True, null=True, default=1, on_delete=True)
email = models.EmailField(blank=True, null=True)
parking_on = models.DateField(auto_now=False, auto_now_add=False, blank=True, null=True,
help_text='Alege data cand doresti sa vii in office',)
parking_off = models.DateField(auto_now=False, auto_now_add=False, blank=True, null=True,
help_text='Alege Data Plecarii')
numar_masina = models.CharField(max_length=8, default="IF77WXV", blank=True, null=True,
help_text='Introdu Numarul Masinii')
location = models.CharField(max_length=3, blank=True, default="P1", null=True, choices=PARKING_PLOT,
help_text='Alege Locul de Parcare Dorit')
updated = models.DateTimeField(
auto_now=True, auto_now_add=False, blank=True, null=True)
timestamp = models.DateTimeField(
auto_now=False, auto_now_add=True, blank=True, null=True)
venire = models.TimeField(default=time(
9, 00), auto_now=False, auto_now_add=False, help_text='Alege Ora Venirii')
plecare = models.TimeField(default=time(
18, 00), auto_now=False, auto_now_add=False, help_text='Alege Ora Plecarii')
objects = ParcareManager()
def __str__(self):
return self.location + " | " + str(self.parking_on) + " | " + str(self.parking_off)
class Meta:
verbose_name_plural = "parcare"
ordering = ["-parking_on"]
unique_together = ("parking_on", "location")
def clean(self):
if self.parking_on == today: # merge--vedem dak parcam azi
raise ValidationError(
{'parking_on': _('Please book for a date starting tomorrow')})
if self.parking_off < self.parking_on: # merge-vedem daca bookam in trecut
raise ValidationError(
{'parking_off': _('You cant book for a past date!')})
def save(self):
list = []
d = self.parking_on
while d <= self.parking_off:
list.append(
Parcare(user=self.user,
email=self.email,
parking_on=d,
parking_off=d,
location=self.location
)
)
d = d + timedelta(days=1)
Parcare.objects.bulk_create(list)
from django.contrib import admin
from .models import Parcare
class ParcareModelAdmin(admin.ModelAdmin):
list_display = ["user", "location",
"parking_on", "parking_off", "venire", "plecare", "timestamp"]
list_display_links = ["user", "location"]
list_editable = ["parking_off", "parking_on", "venire", "plecare"]
list_filter = ["parking_on", "location", "email"]
search_fields = ["location", "parking_on"]
date_hierarchy = 'parking_on'
class Meta:
model = Parcare
def get_form(self, request, obj=None, **kwargs):
form = super().get_form(request, obj, **kwargs)
if not obj:
user = request.user
form.base_fields['user'].initial = user
form.base_fields['email'].initial = user.email
return form
admin.site.register(Parcare, ParcareModelAdmin)
Thank you in advance!

I just have some thoughts about this problem.
I think the best solution is replace DateField on DateTimeField. Then you can use in clean method something like this (just like an example with bad performance):
def clean(self):
qs = Parcare.objects.filter(location=self.location)
.filter(parking_on__gt=self.parking_on)
.filter(parking_off__lt=self.parking_on)
qs2 = Parcare.objects.filter(location=self.location)
.filter(parking_on__gt=self.parking_off)
.filter(parking_off__lt=self.parking_off)
if len(qs) > 0 or len(qs2) > 0:
raise ValidationError('Time is booked already')
if self.parking_on < datetime.now(): # merge--vedem dak parcam azi
raise ValidationError(
{'parking_on': _('Please book for a time more than now')})
if self.parking_off < self.parking_on: # merge-vedem daca bookam in trecut
raise ValidationError(
{'parking_off': _('You cant book for a past date!')})

Related

django serializer error: images_data = self.context['request'].FILES KeyError: 'request'

models.py
#
from django.db import models
from user.models import User
from chat.models import TradeChatRoom, AuctionChatRoom
class Goods(models.Model):
class Meta:
db_table = 'Goods'
ordering = ['-created_at'] # 일단 추가해뒀습니다
seller = models.ForeignKey(User, on_delete=models.CASCADE, related_name='sell_goods')
buyer = models.ForeignKey(User, on_delete=models.CASCADE, related_name='buy_goods', null=True)
trade_room = models.ForeignKey(TradeChatRoom, on_delete=models.CASCADE)
auction_room = models.ForeignKey(AuctionChatRoom, on_delete=models.CASCADE)
title = models.CharField(max_length=256)
content = models.TextField()
category = models.CharField(max_length=32)
status = models.BooleanField(null=True)
predict_price = models.IntegerField()
start_price = models.IntegerField()
high_price = models.IntegerField(null=True)
start_date = models.DateField(null = True)
start_time = models.DateTimeField(null=True)
created_at = models.DateTimeField(auto_now_add=True)
like = models.ManyToManyField(User, related_name='like_goods', null=True)
class GoodsImage(models.Model):
class Meta:
db_table = "GoodsImage"
goods = models.ForeignKey(Goods, on_delete=models.CASCADE)
image = models.ImageField(upload_to='goods/')
serializer.py
from rest_framework import serializers
from .models import Goods,GoodsImage
class GoodImageSerializer(serializers.ModelSerializer):
image = serializers.ImageField(use_url=True)
def get_image(self, obj):
image = obj.goods_set.all()
return GoodsPostSerializer(instance=image, many = True, context = self.context)
class Meta:
model = GoodsImage
field =('image',)
class GoodsPostSerializer(serializers.ModelSerializer):
image = GoodImageSerializer(many=True, read_only = True)
class Meta:
model = Goods
fields = (
'seller', 'buyer','auction_room','title','content',
'category','status','predict_price','start_price','high_price',
'trade_room','start_date','start_time','created_at','like','image',
)
read_only_fields = ("seller",)
def create(self, validated_data):
goods = Goods.objects.create(**validated_data)
images_data = self.context['request'].FILES
for image_date in images_data.getlist('image'):
GoodsImage.objects.create(goods = goods, image = image_date)
return goods
error
images_data = self.context['request'].FILES
KeyError: 'request'
I want to save multiple images, but I keep getting an error. I don't know what to do anymore.
I searched for a method and followed it, but it seems that I am the only one who gets an error.
Please help if you know how to solve this problem.
And I want to know if it is correct to put it in a list like "image":["12.jpeg,"13.jpeg] when inserting multiple images through postman.
It's hard not being able to solve this problem. please help me if you know the answer
Change GoodImageSerializer calling this:
GoodImageSerializer(instance=images, many = True, context={'request': request})
Then change your GoodsPostSerializer's create method like this:
def get_image(self, obj):
image = obj.goods_set.all()
request = self.context['request']
return GoodsPostSerializer(instance=image, many = True, context={'request': request})

Django erroneously triggers the post_save signal on instance delete

I'm trying to send an email notification when an Article instance is created or modified. I use signals and send_mail. Everything works great while I just create and modify articles. But, if I delete an Article, I've got a notification that it was updated! This is incorrect behavior. What can be a reason (and solution)?
models.py
from django.core.mail import send_mail
from django.db.models.signals import post_save
from django.dispatch import receiver
# ...
class Article(TimeStampedModel):
title = models.CharField(_('Title'), max_length=255, db_index=True)
slug = AutoSlugField(_('Slug'), unique_with=['year', 'month'], populate_from="title", db_index=True)
picture = models.ImageField(verbose_name=_('Image'),
upload_to = upload_to)
category = models.ForeignKey('Category', blank=True, null=True,
on_delete=models.SET_NULL, db_index=True, related_name='articles')
author = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL)
annotation = models.TextField(_('Brief annotation'), blank=True) # Brief annotation (1 paragraph)
date = models.DateTimeField(_('Date'), default=timezone.now, db_index=True)
modified = models.DateTimeField(_('Modified'), db_index=True, auto_now=True)
year = models.PositiveIntegerField(_('Year'), db_index=True, blank=True)
month = models.CharField(_('Month'), db_index=True, max_length=2, blank=True)
is_published = models.BooleanField(_('Is published'), default=False)
def delete(self, *args, **kwargs): # Remove the media files of the article together with their folder
from django.core.files.storage import default_storage
if self.picture:
with contextlib.suppress(FileNotFoundError):
default_storage.delete( self.picture_thumbnail.path )
self.picture.delete()
path = os.path.join(settings.MEDIA_ROOT, settings.CKEDITOR_UPLOAD_PATH, 'news', str(self.year), str(self.month), str(self.slug))
if os.path.isdir(path):
shutil.rmtree(path) # Remove folder
super().delete(*args, **kwargs)
def __str__(self):
# Потом допилить, чтобы год и месяц тоже выводился
return self.title # There can be other ways: year, month and slugname, for example
def get_absolute_url(self):
return reverse('news:detail', kwargs={'year': self.year, 'month': self.month, 'slug':self.slug})
class Meta:
unique_together = ("year", "month", "slug")
ordering = ['-date', '-modified',]
class Comment(models.Model):
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='comments')
author = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL)
text = RichTextField(_('Comment'), config_name = 'tiny')
active = models.BooleanField(_('Visible'), default=True, db_index=True)
created = models.DateTimeField(_('Created at'), auto_now_add=True, db_index=True)
updated = models.DateTimeField(_('Updated at'),auto_now=True)
def __str__(self):
return f'Comment by {self.author} on {self.article}'
class Meta:
#verbose_name_plural = _('Comments')
#verbose_name = _('Comment')
ordering = ['-created']
#receiver(post_save, sender=Article)
def email_on_article_change(sender, instance, created, **kwargs):
# if a new officer is created, compose and send the email
if created: # created -- это булевская переменная, чтобы отличать новые записи от изменений старых
action = "created"
else:
action = "updated"
title = instance.title if instance.title else "no title given"
annotation = instance.annotation if instance.annotation else "no annotation given"
author = instance.author if instance.author else 'no author assignment'
subject = 'TITLE: {0}, ANNOTATION {1}, AUTHOR: {2}'.format(title, annotation, author)
message = 'A New Article has been ' + action + '!\n'
message += 'TITLE: ' + str(title) + '\n' + 'ANNOTATION: ' + str(annotation) + '\n' + 'AUTHOR: ' + str(author) + '\n'
message += '--' * 30
send_mail(
subject,
message,
'mail#raptors.ru',
['michael_romanov#inbox.ru', 'romanov-spm#yandex.ru'],
fail_silently=False,
)
P. S. I've read on this forum that sometimes this behavior can be caused by foreign keys. My Article model has two foreign keys, author and category. Also, there is a Comment model which has a foreign key 'article'. But I can't see how they can cause this problem and how to solve it. Please help me anybody!

How to set specific conditions in models.py while entering data in admin panel?

room_category = models.ForeignKey(Cat, on_delete=models.CASCADE)
number = models.IntegerField(unique=True)
people = models.IntegerField()
picture = models.ImageField(upload_to = 'room/', null=True, blank=True)
actual_price = models.IntegerField()
offer_price = models.IntegerField()
def __str__(self):
return '%d : %s with People : %d' % (self.number, self.room_category, self.people)
I want to set a condition in offer_price table that offer_price < actual_price. It should show an error while entering data in the admin panel itself.
You can add validation in the .clean(…) method [Django-doc], and add a database constraint with Django's constraint framework:
from django.core.exceptions import ValidationError
from django.db.models import F, Q
class YourModel(models.Model):
# …
actual_price = models.IntegerField()
offer_price = models.IntegerField()
def clean(self, *args, **kwargs):
if self.actual_price <= self.offer_price:
raise ValidationError('offer price should be lower than the actual price')
return super().clean(*args, **kwargs)
class Meta:
constraints = [
models.CheckConstraints(
check=Q(offer_price__lt=F('actual_price')),
name='lower_than_actual_price'
)
]

Django multi language model / filter post by languages

There is my simple blog model;
class Article(models.Model):
author = models.ForeignKey("auth.User",on_delete = models.CASCADE, verbose_name="Author")
title_en = models.CharField(max_length = 120, verbose_name="Title_En")
title_de = models.CharField(max_length = 120, verbose_name="Title_De")
category = models.ForeignKey('Category', on_delete = models.CASCADE, null=True, blank=True)
content_en = RichTextField(verbose_name="Content_En")
content_de = RichTextField(verbose_name="Content_De")
created_date = models.DateTimeField(auto_now_add=True, verbose_name="Created Date")
image = models.ImageField(blank=True, null=True, verbose_name="Add Photo (.jpg .png)")
slug = models.SlugField(unique=True, max_length = 130)
def __str__(self):
return self.title
I use url's with language like this;
domainname.com/en/
domainname.com/de/
For example, how can I show only the contents that belong to title_de and content_de in the domainname.com/de urls?
How can I do filtering with language? Is there an easy solution to this?
(I usage django 2.1.2. i try django-modeltranslation or others dont work this django version...)
Thanks...
You can create a descriptor class that wraps the translated fields e.g.,
from django.utils import translation
class TranslatedField:
def __init__(self, field_name):
self.partial_field_name = field_name
def __get__(self, obj, objtype):
return getattr(obj, self.field_name)
def __set__(self, obj, value):
return setattr(obj, self.field_name, value)
#property
def field_name(self):
language_code = translation.get_language()
rerurn self.partial_field_name + '_' + language_code
class Article(models.Model):
title_en = models.CharField(max_length=120)
title_de = models.CharField(max_length=120)
title = Translated Field('title')
Then you can do
article = Article.objects.create(
title_en='In english',
title_de='In German'
)
print(article.title) # 'In english'
translation.set_language('de')
print(article.title) # 'In German'
article.title = 'In German!'
print(article.title) # 'In German!'
translation.set_language('en')
print(article.title) # 'In english'
(Untested, so there may be typos)
I would use something out of the box like https://github.com/deschler/django-modeltranslation
Filtering based on keyward argument is one of the option for this problem.I would prefer to add a language field 'EN' or 'DE' rather than repeating same kind of title and content field and filtering based on that. For example,
Article Model can be like
class Article(models.Model):
LANGUAGE_TYPES = (
('EN', 'EN'),
('DE', 'DE'),
)
author = models.ForeignKey("auth.User",on_delete = models.CASCADE, verbose_name="Author")
title = models.CharField(max_length = 120, verbose_name="Title")
category = models.ForeignKey('Category', on_delete = models.CASCADE, null=True, blank=True)
content = RichTextField(verbose_name="Content")
created_date = models.DateTimeField(auto_now_add=True, verbose_name="Created Date")
image = models.ImageField(blank=True, null=True, verbose_name="Add Photo (.jpg .png)")
slug = models.SlugField(unique=True, max_length = 130)
language = models.CharField(
max_length=10, choices=LANGUAGE_TYPES)
def __str__(self):
return self.title
Our urls can be like
from django.urls import path
from .views import (ArticleView)
urlpatterns = [
path('article/<slug:type>/', ArticleView.as_view(), name='article'),
]
And Our view can be like
from rest_framework import views, status
from .serializers import ArticleSerializer
from .models import Article
class ArticleView(views.APIView):
def get(self, request):
article_language_type = self.kwargs.get('type', None)
articles = Article.objects.filter(language=article_language_type)
serializer = ArticleSerializer(articles, many=True)
if serializer.is_valid():
return Response(serializer.data, status=status.HTTP_200_OK)
else:
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

more than 1 foreign key

I have the following models: http://slexy.org/view/s20T8yOiKZ
from mxutils.cms_services import generate_secid
from django.db import models
from django.contrib import admin
from django import forms
class World(models.Model):
title = models.CharField(max_length=150)
secid = models.SlugField(max_length=1000, editable=False)
elements = models.ManyToManyField("Element", related_name='elements', blank=True, null=True)
metadata = models.OneToOneField("Category_metadata", blank=True, null=True)
def save(self):
if not self.pk:
super(World, self).save()
self.secid = generate_secid(self.title, self.pk, World.objects.all())
return super(World, self).save()
def __unicode__(self):
return "%s" % self.title
class Element(models.Model):
parent = models.ForeignKey(World, related_name='element_parent')
world = models.ForeignKey(World, related_name='world', blank=True, null=True)
item = models.ForeignKey("Item", blank=True, null=True)
value = models.DecimalField(default=0, max_digits=5, decimal_places=3)
def save(self):
if self.world and self.item:
return None
elif not self.world and not self.item:
return None
else:
return super(Element, self).save()
def __unicode__(self):
if self.world:
return "%s" % self.world.title
else:
return "%s" % self.item.title
class ElementInline(admin.TabularInline):
model = Element
extra=1
class WorldAdmin(admin.ModelAdmin):
inlines = [ElementInline,]
list_display = ('title',)
ordering = ['title']
search_fields = ('title',)
When I try to click add button for worlds in admin page it shows me the following error:
class 'cms_sample.world_models.Element' has more than 1 ForeignKey to class 'cms_sample.world_models.World'.
I think it's something to do with inline.
What can it be?
Django doesn't know which of the two foreign keys (parent and world) is to be inlined using the ElementInline.
class ElementInline(admin.TabularInline):
model = Element
fk_name = 'parent' #or 'world', as applicable.
extra=1