Django erroneously triggers the post_save signal on instance delete - django

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!

Related

send email on model create - django

I want to send an email automatically when I create an invoice. Is that possible? What I'm after. I have a invoice model and I put send_email code in it "def save(self):", I'm making perfex CRM invoice system, so I'm using the foregin key in invoice model to get a customer, but whenever I create new invoice it says "The invoices “INV-b066” was added successfully." but in invoice model it shows nothing like it's empty model I even tried to open invoice using index number and restarted the server and migrations stuff and it didn't worked but if I remove def save(self): function it works perfectly fine I'm trying to send an email automatically on model create
Customer model
class Customers(models.Model):
Your_Name = models.CharField(max_length=220)
Email_Address = models.EmailField(max_length=220)
Profession = models.CharField(max_length=220)
phone = PhoneNumberField(unique=True)
No_of_Persons = models.IntegerField()
Packages = models.CharField(choices=PAKAGES, max_length=100)
Address = models.CharField(max_length=220)
City = models.CharField(choices=CITIES, max_length=10)
Time = models.CharField(choices=TIME, max_length=10)
Date = models.DateTimeField()
Message = models.TextField()
def __str__(self):
return f'{self.Your_Name}'
Invoice model
class Invoice(models.Model):
Customer = models.ForeignKey(Customers, on_delete=models.CASCADE)
Invoice_Number = models.CharField(default=inv_num, max_length=10)
Recurring = models.CharField(choices=Recurrings, max_length=12)
Invoice_date = models.DateField()
Due_date = models.DateField()
Packages = models.CharField(choices=PAKAGES, max_length=100)
Package_name = models.CharField(max_length=50)
Package_description = models.TextField()
Package_Quantity = models.IntegerField()
Package_Price = models.IntegerField()
def __str__(self):
return f'{self.Invoice_Number}'
def save(self):
send_mail(
'Subject',
'message.',
'email#example.com',
['*****#gmail.com'],
fail_silently=False,
)
You have to actually save the invoice like this:
def save(self):
send_mail(
'Subject',
'message.',
'email#example.com',
['*****#gmail.com'],
fail_silently=False,
)
return super(Invoice, self).save()
Also as a part of suggestion it is better to send email as separate task or service, you can use django signals or celery for that.

Django Rest Framework - Add Data and return it

I am new to Django Rest Framework and trying to create add data upon request and return the data after creating.
My URL is path('quiz/<str:classId>/<str:subject>/<str:chapter>', CreateQuiz.as_view()),
I want to add a row to Quiz Model using the parameters in the URL and return it.
How should I approach this ? I tried adding it in get_queryset method and it runs twice. Any guide to this specific problem ? Please help.
Serializer:
class QuizSerializer(serializers.ModelSerializer):
#get_questions = QuestionSerializer(many=True)
class Meta:
model = Quiz
fields=(
'id',
'title',
'description',
'url',
'category',
#'sub_category',
'random_order',
'pass_mark',
'draft',
'durationtest',
#'get_questions',
#'get_max_score',
#'questionlist',
)
View:
class CreateQuiz(generics.ListCreateAPIView):
serializer_class = QuizSerializer
def get_queryset(self):
classId = self.kwargs.get('classId',None)
subject = self.kwargs.get('subject',None)
category = Category.objects.filter(class_id=classId,category=subject).values_list('id',flat=True)[0]
# chapter = self.kwargs.get('chapter',None)
subcategory=self.kwargs.get('chapter',None)
subcat = SubCategory.objects.filter(id=subcategory).values_list('sub_category',flat=True)[0]
total_marks = 30
questionIDs = Question.objects.raw('''somesql
''',params=[category,subcategory,total_marks,category,subcategory,total_marks])
#
# and qq.sub_category_id IN %s
questions= MCQuestion.objects.filter(question_ptr_id__in=questionIDs).prefetch_related('answer_set__question')
essayquestions= Essay_Question.objects.filter(question_ptr_id__in=questionIDs)
user = User.objects.get(id=1)
if MCQuestion.objects.filter(question_ptr_id__in=questionIDs).prefetch_related('answer_set__question').exists():
print("create quiz")
quiz = Quiz()
quiz.category_id = category
quiz.title = "Practice"
quiz.owner= user
quiz.single_attempt = False
quiz.durationtest="10:00"
quiz.random_order=True
quiz.save()
quizid = Quiz.objects.filter(id=quiz.id).values_list('id',flat=True)[0]
quiz = Quiz.objects.get(id=quiz.id)
quiz.title = "Practice Exam : "+ quiz.category.class_id.classVal +" "+quiz.category.category +" ("+subcat+") "
quiz.save()
with connection.cursor() as cursor:
cursor.execute("INSERT INTO quiz_subcategory_quiz(subcategory_id,quiz_id) VALUES (%s,%s)",(subcategory,quiz.id,))
for obj in questionIDs:
if Question.objects.filter(id=obj.id,quiz__id=quiz.id).exists():
print("Do nothing")
else:
question = Question(id=obj.id)
question.quiz.add(quiz.id)
question.save()
obj, created = UserQuiz.objects.update_or_create(
user_id = user.id,quiz_id=quiz.id
)
return Quiz.objects.filter(id=quizid)
else:
response = JsonResponse({"error": "there was an error"})
response.status_code = 403 # To announce that the user isn't allowed to publish
return response
return Quiz.objects.filter(id=quizid)
Model:
class Quiz(models.Model):
title = models.CharField(
verbose_name=_("Title"),
max_length=300, blank=False)
description = models.TextField(
verbose_name=_("Description"),
blank=True, help_text=_("a description of the quiz"))
url = models.SlugField(
max_length=60, blank=False,
help_text=_("a user friendly url"),
verbose_name=_("user friendly url"),default=uuid.uuid4)
category = models.ForeignKey(
Category, null=True, blank=True,
verbose_name=_("Category"), on_delete=models.CASCADE)
random_order = models.BooleanField(
blank=False, default=False,
verbose_name=_("Random Order"),
help_text=_("Display the questions in "
"a random order or as they "
"are set?"))
max_questions = models.PositiveIntegerField(
blank=True, null=True, verbose_name=_("Max Questions"),
help_text=_("Number of questions to be answered on each attempt."))
answers_at_end = models.BooleanField(
blank=False, default=False,
help_text=_("Correct answer is NOT shown after question."
" Answers displayed at the end."),
verbose_name=_("Answers at end"))
exam_paper = models.BooleanField(
blank=False, default=True,
help_text=_("If yes, the result of each"
" attempt by a user will be"
" stored. Necessary for marking."),
verbose_name=_("Exam Paper"))
single_attempt = models.BooleanField(
blank=False, default=False,
help_text=_("If yes, only one attempt by"
" a user will be permitted."
" Non users cannot sit this exam."),
verbose_name=_("Single Attempt"))
pass_mark = models.SmallIntegerField(
blank=True, default=0,
verbose_name=_("Pass Mark"),
help_text=_("Percentage required to pass exam."),
validators=[MaxValueValidator(100)])
success_text = models.TextField(
blank=True, help_text=_("Displayed if user passes."),
verbose_name=_("Success Text"))
fail_text = models.TextField(
verbose_name=_("Fail Text"),
blank=True, help_text=_("Displayed if user fails."))
draft = models.BooleanField(
blank=True, default=False,
verbose_name=_("Draft"),
help_text=_("If yes, the quiz is not displayed"
" in the quiz list and can only be"
" taken by users who can edit"
" quizzes."))
DIFFICULTY_LEVEL=[
('1','Beginner'),
('2','Intermediate'),
('3','Advanced'),
('4','Exppert'),
]
difficulty = models.CharField(
max_length=2,
choices=DIFFICULTY_LEVEL,
default=2,
)
durationtest = models.CharField(max_length=6,blank=True,null=True)
owner = models.ForeignKey(User, null=True, blank=True, on_delete=models.SET_NULL)
created_at = models.DateTimeField(auto_now_add=True,blank=True,null=True,)
history = HistoricalRecords()
There is a lot of confusion in what you're trying to achieve. The get_queryset method is meant to filter the data you want to send. In your case, it will just indicate which Quizz instances will be sent when you send a GET request (list).
Now, for what you want to do, sending a POST request and creating an instance from the URL parameters is absolutely not recommended. You must send information via the data part of your request. Anyway, in both cases you need to override the create method of your view.
class CreateQuiz(generics.ListCreateAPIView):
serializer_class = QuizSerializer
def get_queryset(self):
# return a queryset here, but don't create anything !
def create(self, request, *args, **kwargs):
# your logic resulting in having a variable your_data
serializer = QuizSerializer(data=your_data)
if serializer.is_valid():
serializer.save()
# return the data of your new instance here)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

While saving Django getting "This choice is not one of the available choices"

I am trying to save a form which have ForeignKey (purchaseContractID).Here is my contract Model
class contracts(models.Model):
productDetailID=models.ForeignKey('Inventory.productDetails',related_name='+',on_delete=models.CASCADE,verbose_name='Select Product',default=None)
supplierID=models.ForeignKey(suppliers,on_delete=models.CASCADE,verbose_name='Select Supplier',default=None)
totalUnits=models.IntegerField(verbose_name='Total Units',editable=False,default=None)
ratePerUnit=models.IntegerField(verbose_name='Rate Per Unit',default=None)
saleTax=models.IntegerField(verbose_name='Sale Tax',default=None)
incomeTax=models.IntegerField(verbose_name='Income Tax',default=None)
saleTaxwithHeld=models.IntegerField(verbose_name='Sale Tax with Held',default=None)
startDate=models.DateField(verbose_name='Start Date',default=None)
endDate=models.DateField(verbose_name='End Date',default=None)
manulContractNumber=models.IntegerField(verbose_name='Manul Contract Number',default=None)
paymentDays=models.IntegerField(verbose_name='Payment Days',default=None)
remarks=models.CharField(verbose_name='Remarks',max_length=100,default=None)
dateOfEntry=models.DateField(editable=False,default=datetime.now())
def __str__(self):
return str(self.productDetailID.name)
here is my inventoryIn Model which foreignKey of PurchaseContract
class inventoryIn(models.Model):
supplierID=models.ForeignKey('Purchase.suppliers',editable=False,on_delete=models.CASCADE,verbose_name='Supplier')
productID=models.ForeignKey(products,editable=False,on_delete=models.CASCADE)
purchaseContractID=models.ForeignKey('Purchase.contracts',on_delete=models.CASCADE,verbose_name='Contract ID')
unitsIn=models.IntegerField(verbose_name='Enter No of Bags')
MYCHOCIES = (('orginal', 'ORGINAL'), ('dummy', 'DUMMY'))
doType = models.CharField(blank=True, choices=MYCHOCIES, verbose_name='Select DO Type', max_length=20)
doID=models.IntegerField(verbose_name='Do No')
doImage=models.ImageField(upload_to='doImage/%Y/%m/%d',verbose_name='Do Image')
invoiceID=models.IntegerField(verbose_name='Invoice No')
invoiceImage=models.ImageField(upload_to='inventoryIn/%Y/%m/%d')
agingDate=models.DateField(verbose_name='Receiving Date')
labReportImage = models.ImageField(upload_to='labReportImage/%Y/%m/%d', blank=True,verbose_name='Lab Report Image')
enterPaymentDays = models.IntegerField(verbose_name='Enter Payment Days', blank=True, default=None)
dateOfEntry=models.DateField(default=datetime.now())
def __str__(self):
return self.supplierID
here is my admin.py where i am adding admin form and also adding a js that add some fields dynamical.
class inventoryInAdmin(admin.ModelAdmin):
fields = ['purchaseContractID','unitsIn','doType','doID','doImage','invoiceID','invoiceImage','agingDate','labReportImage','enterPaymentDays']
class Media:
js = ('js/addInventory.js',)
admin.site.register(inventoryIn,inventoryInAdmin)
it is not allowing me to submit form and giving me error "Select a valid choice. That choice is not one of the available choices."
I have resolved this issue by adding the save method at InventoryIn Model.
class inventoryIn(models.Model):
supplierID=models.ForeignKey('Purchase.suppliers',editable=False,on_delete=models.CASCADE,verbose_name='Supplier')
productID=models.ForeignKey(products,editable=False,on_delete=models.CASCADE)
purchaseContractID=models.ForeignKey('Purchase.contracts',on_delete=models.CASCADE,verbose_name='Contract ID')
unitsIn=models.IntegerField(verbose_name='Enter No of Bags')
MYCHOCIES = (('orginal', 'ORGINAL'), ('dummy', 'DUMMY'))
doType = models.CharField(blank=True, choices=MYCHOCIES, verbose_name='Select DO Type', max_length=20)
doID=models.IntegerField(verbose_name='Do No')
doImage=models.ImageField(upload_to='doImage/%Y/%m/%d',verbose_name='Do Image')
invoiceID=models.IntegerField(verbose_name='Invoice No')
invoiceImage=models.ImageField(upload_to='inventoryIn/%Y/%m/%d')
agingDate=models.DateField(verbose_name='Receiving Date')
labReportImage = models.ImageField(upload_to='labReportImage/%Y/%m/%d', blank=True,verbose_name='Lab Report Image')
enterPaymentDays = models.IntegerField(verbose_name='Enter Payment Days', blank=True, default=None)
dateOfEntry=models.DateField(default=datetime.now())
def __str__(self):
return str(self.supplierID)
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
from Purchase.models import contracts,suppliers
contract = contracts.objects.values('supplierID', 'productDetailID').filter(id=self.purchaseContractID.id)
supplier=contract[0].get("supplierID")
product=contract[0].get("productDetailID")
self.supplierID=suppliers.objects.get(id=supplier)
self.productID=products.objects.get(productDetailsID=product)
super(inventoryIn,self).save()
and i also had made my two fields editable=False
supplierID=models.ForeignKey('Purchase.suppliers',editable=False,on_delete=models.CASCADE,verbose_name='Supplier')
productID=models.ForeignKey(products,editable=False,on_delete=models.CASCADE)

Django Admin Inline returning empty extra instances

First time posting, having a bit of a weird issue with Django's Admin TabularInline. Couldn't seem to find the problem in any searches.
When I add a value - in this case a Financial Quote - and save the entry, the page will refresh having added the instance and an additional 2 entries that have empty values in every field.
The same happens if I flag them for deletion from the admin page. It deletes all entries and then adds 3 more in the place of the previous ones.
The same happens with the Invoice model (which is a similar model) but not with the Purchase models which behaves as expected. This leads me to think i've done something odd when I've written the models.
Image attached to show the result.
Hopefully someone can see where i've gone wrong
Thanks!
models.py
class Quote(models.Model):
job = models.ForeignKey(Job, related_name="quotes", on_delete=models.CASCADE)
number = models.AutoField(primary_key=True)
currency = models.ForeignKey(Currency, blank=True, null=True)
amount = models.DecimalField(max_digits=20, decimal_places=2, default="0.00", verbose_name="Amount Invoiced")
created = models.DateTimeField(auto_now=False, auto_now_add=True)
created_by = models.ForeignKey(Profile, related_name='quoted', blank=True, null=True, on_delete=models.SET_NULL)
sent = models.BooleanField(default=False)
superceded = models.BooleanField(default=False)
tax = models.DecimalField(max_digits=20,decimal_places=2,default=20.00, verbose_name="Tax Rate")
def __unicode__(self):
return self.created.strftime("%B %d, %Y") + " | " + u'%s' % (self.currency) + str(self.amount)
def readable_date(self):
return self.created.strftime("%B %d, %Y")
class Invoice(models.Model):
job = models.ForeignKey(Job, related_name="invoices", blank=True, null=True, on_delete=models.SET_NULL)
number = models.AutoField(primary_key=True)
currency = models.ForeignKey(Currency, blank=True, null=True)
amount = models.DecimalField(max_digits=20, decimal_places=2, default="0.00", verbose_name="Amount Invoiced")
created = models.DateTimeField(auto_now=False, auto_now_add=True)
created_by = models.ForeignKey('profiles.Profile', related_name='invoiced', blank=True, null=True, on_delete=models.SET_NULL)
paid = models.BooleanField(default=False)
sent = models.BooleanField(default=False)
superceded = models.BooleanField(default=False)
tax = models.DecimalField(max_digits=20,decimal_places=2,default=20.00, verbose_name="Tax Rate")
def __unicode__(self):
return self.created.strftime("%B %d, %Y") + " | " + u'%s' % (self.currency) + str(self.amount)
def readable_date(self):
return self.created.strftime("%B %d, %Y")
def get_day(self):
return self.created.strftime("%d")
def get_month(self):
return self.created.strftime("%b")
admin.py
from finance.models import Purchase, Quote, Invoice
from django.contrib import admin
from .models import Job
class QuoteInline(admin.TabularInline):
model = Quote
class InvoiceInline(admin.TabularInline):
model = Invoice
class PurchaseInline(admin.TabularInline):
model = Purchase
class JobModelAdmin(admin.ModelAdmin):
list_display = [
'job_number',
'brand',
'job_name',
'client',
'account_manager',
'last_updated_by',
'updated',
'status',
]
list_display_links = ['job_name']
list_filter = ['client']
inlines = [
QuoteInline,
PurchaseInline,
InvoiceInline
]
Example of issue in admin page
In your inline classes set extra=0. I guess you have this problem because you have fields with default values and no any required fields in auto-created instances, so you accidentially save them, and django didn't raise any errors.

Modelserializer using kwargs to get FK object

I'm creating a Django (1.8) webapp that saves racing laptimes and scoreboards. The database is populated using an API built using Django Rest Framework. It's the first time I'm trying to build a proper api using rest framework.
A quick overview of the models:
Event, A racing event/weekend
Session, A single race/practice/quali - FK Event
Car, A car taking part in a session - FK Session
Lap, Laps for specific car - FK Car
The Event is created manually, but the rest is supposed to be "dynamic" (get or create)
Right now I'm trying to create a new car using my API, but I'm stuck. To get the cars event and session I'm trying to use the url;
/api/results/skrotbilsracet-29042016/r1/cars/
The idea is to post data to this url and "get or create" a new car object.
To get the correct session object for the new car session FK, I need to use a custom function that takes the kwargs and tries to find the session.
The more I read about how to solve this, the more confused I get.
Could someone push me in the right direction?
This is my latest attempt at solving this, which just gives me "{"session":["This field is required."]}"
models.py
class Session(models.Model):
session_types = (
('p', 'Practice'),
('q', 'Qualification'),
('r', 'Race')
)
event_id = models.ForeignKey(Event, related_name='sessions')
name = models.CharField(max_length=2, blank=True)
current_session = models.BooleanField(default=True)
session_type = models.CharField(max_length=2,
choices=session_types)
started = models.DateTimeField(auto_now_add=True)
ended = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['started']
def save(self):
if not self.name:
# Get number of sessions
session_count = Session.objects.filter(event_id=self.event_id)\
.filter(session_type=self.session_type)\
.count()
session_count += 1
self.name = self.session_type + str(session_count)
super(Session, self).save()
def __unicode__(self):
string = self.started.strftime("%d-%m-%Y %H:%M") + ' - '
string += self.name.upper()
return(string)
class Car(models.Model):
session = models.ForeignKey(Session, related_name='cars')
number = models.IntegerField()
full_name = models.CharField(max_length=256, blank=True)
short_name = models.CharField(max_length=256, blank=True)
race_class = models.CharField(max_length=50, blank=True)
best_lap = models.IntegerField(blank=True, null=True)
best_lap_time = models.CharField(max_length=20, blank=True)
best_sector1 = models.CharField(max_length=20, blank=True)
best_sector2 = models.CharField(max_length=20, blank=True)
best_sector3 = models.CharField(max_length=20, blank=True)
best_speed = models.IntegerField(blank=True, null=True)
pitstops = models.IntegerField(blank=True, null=True)
total_time = models.CharField(max_length=20, blank=True)
transponder = models.CharField(max_length=50, blank=True)
apiUrls.py
urlpatterns = [
url(r'^raceslug/$', raceSlugView.as_view(), name='race-slug'),
url(r'^events/$', eventsView.as_view(), name='event-list'),
url(r'^session/$', getSessionView.as_view(), name='session-pk'),
url(r'^(?P<event_id>[a-z0-9\-]+)/$', eventView.as_view(), name='event-detail'),
url(r'^(?P<event_id>[a-z0-9\-]+)/(?P<name>[a-z0-9\-]+)/$', sessionView.as_view(), name='session-detail'),
url(r'^(?P<event_id>[a-z0-9\-]+)/(?P<name>[a-z0-9\-]+)/cars/$', carsView.as_view(), name='car-list'),
url(r'^(?P<event_id>[a-z0-9\-]+)/(?P<name>[a-z0-9\-]+)/(?P<number>[0-9]+)/$', carView.as_view(), name='car-detail'),
]
urlpatterns = format_suffix_patterns(urlpatterns)
api.py
class carsView(generics.ListCreateAPIView):
serializer_class = carSerializer
def get_session(self, event_id, name):
print('Getting session')
# Get event object
try:
event = Event.objects.get(event_id=event_id)
print('Found event')
except ObjectDoesNotExist:
print('Did not find event')
return
# Get session object
try:
session = event.sessions.get(name=name)
print('Found session: ', session)
return session
except ObjectDoesNotExist:
print('Did not find session')
return
def get_queryset(self):
print('Getting queryset')
print('event_id: ' + self.kwargs['event_id'])
print('name: ' + self.kwargs['name'])
session = self.get_session(self.kwargs['event_id'], self.kwargs['name'])
return(Car.objects.filter(session=session.pk))
def perform_create(self, serializer):
print('Creating new car')
session = self.get_session(self.kwargs['event_id'], self.kwargs['name'])
serializer.save(session=session)
serializers.py
class carSerializer(serializers.ModelSerializer):
laps = lapSerializer(many=True, read_only=True)
class Meta:
model = Car
fields = (
'session',
'number',
'full_name',
'short_name',
'race_class',
'best_lap',
'best_lap_time',
'best_sector1',
'best_sector2',
'best_sector3',
'best_speed',
'pitstops',
'total_time',
'transponder',
'laps')
Solution:
This is what I actually changed to get it working.
api.py
from rest_framework.serializers import ValidationError
class carsView(generics.ListCreateAPIView):
...
def perform_create(self, serializer):
print('Creating new car')
session = self.get_session(self.kwargs['event_id'], self.kwargs['name'])
number = self.request.POST.get('number')
car = session.cars.filter(number=number)
if car.exists():
raise ValidationError('Car already exists')
serializer.save(session=session)
serializers.py
class carSerializer(serializers.ModelSerializer):
laps = lapSerializer(many=True, read_only=True)
session = serializers.StringRelatedField(required=False)
...
I see that you're creating your session ID there:
def get_queryset(self):
...
session = self.get_session(self.kwargs['event_id'], self.kwargs['name'])
return(Car.objects.filter(session=session.pk))
Then you don't need it in a serializer, only in a model. So you can set it a snot required in a serializer, but it will still be required in a model.
I guess this answer could help you: Django REST Framework serializer field required=false