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.
Related
I have 3 django models. Requirement Model has its own fields. RequirementImage and RequirementDOc models have Requirement as foreign key in them which are used for multiple image and multiple document upload. In admin ,I want to show the Requirement along with the images and documents related to requirement. How can i show it in admin panel.
i want to show a view where i can list all fields of Requirement and RequirementImages and RequirementDocs together.
Below is the exact code of models.
class Requirement(models.Model):
name = models.CharField(max_length=100)
description = models.CharField(max_length = 5000)
mobile = models.CharField(max_length=15, null=True, blank=True)
email = models.EmailField(null=True, blank=True)
city = models.CharField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class RequirementImage(models.Model):
requirement = models.ForeignKey('Requirement', on_delete=models.CASCADE)
image = models.ImageField(null=True, blank=True, validators=[
FileMimeValidator()
], upload_to=settings.MEDIA_RELATIVE_ROOT + "requirements/images")
class RequirementDoc(models.Model):
requirement = models.ForeignKey('Requirement', on_delete=models.CASCADE)
requirement_file = models.FileField(null=True, blank=True, upload_to=settings.MEDIA_RELATIVE_ROOT + "requirements/docs")
Python version is 3.7.12 and django version is 3.2.14
in models.py
from django.utils.safestring import mark_safe
class RequirementImage(models.Model):
requirement = models.ForeignKey('Requirement', on_delete=models.CASCADE)
image = models.ImageField(null=True, blank=True, validators=[
FileMimeValidator()
], upload_to=settings.MEDIA_RELATIVE_ROOT + "requirements/images")
def photo_tag(self):
return mark_safe('<img src="/your_path/{0}">'.format(self.image))
photo_tag.short_description = 'Photo of prescription'
photo_tag.allow_tags = True
and when you want to use it in admin
list_display = ('get_photo')
def get_photo(self, obj):
return obj.photo.photo_tag()
get_photo.short_description = 'Photo of prescription'
This is the right answer.we need to use TabularInline.
class RequirementImageInline(admin.TabularInline):
model = RequirementImage
fields = ['image']
extra = 1
class RequirementDocInline(admin.TabularInline):
model = RequirementDoc
fields = ['requirement_file']
extra = 1
class RequirementAdmin(admin.ModelAdmin):
inlines = [RequirementImageInline,RequirementDocInline]
Reference: https://stackoverflow.com/a/74233744/1388835
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!
I have 3 models Product, Photo, and ProductLikeDilike. I am performing left outer join on all the 3 models. First I am joining Product with Photo and then the resultant table(temp) I am joining with ProductLikeDilike. Below is the raw sql.
Note: olx is the name of django app.
data = Product.objects.raw('select * from (select
olx_product.id,olx_product.name,olx_photo.file,olx_photo.cover_photo_flag
from olx_product left outer join olx_photo on
(olx_product.id=olx_photo.reference_id_id) where
olx_photo.cover_photo_flag="yes" or olx_photo.cover_photo_flag is null) as
temp left outer join olx_productlikedislike on
(temp.id=olx_productlikedislike.product_id_id and
olx_productlikedislike.product_liked_by_id_id=2)')
for x in data:
print(x.name)
What I want to understand that when I use any of the above 3 models to run the raw sql why I am getting the same result i.e.
When I do
data = Product.objects.raw('select *.....')
for x in data:
print(x.name)
or
data = Photo.objects.raw('select *......')
for x in data:
print(x.name)
or
data = ProductLikeDislike.raw('select *.....')
for x in data:
print(x.name)
I am getting the same result. Why?
Please help me to understand this.
Below is the models.py file
from django.db import models
from django.urls import reverse
from django.dispatch import receiver
from django.contrib.auth.models import User
class Product(models.Model):
category = models.ForeignKey(Category ,on_delete=models.CASCADE)
name = models.CharField(max_length = 200, db_index = True)
slug = models.SlugField(max_length = 200, db_index = True)
description = models.TextField(blank = True)
price = models.DecimalField(max_digits = 10, decimal_places = 2 )#Not used FloatField to avoid rounding issues
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
contact= models.BigIntegerField(default=None,blank=True, null=True)
created_by = models.CharField(max_length = 200, default=None,blank=True, null=True)
uploaded_by_id = models.IntegerField(default=0)
status = models.IntegerField(default=0) # 0-->Active,1-->Inactive
mark_as_sold = models.IntegerField(default=0) # 0-->not sold,1-->sold
def get_absolute_url(self):
return reverse('olx:edit_product', kwargs={'pk': self.pk})
class Meta:
ordering = ('-created',)
index_together = (('id','slug'),)# we want to query product by id and slug using together index to improve performance
def __str__(self):
return self.name
class Photo(models.Model):
reference_id = models.ForeignKey(Product, null=True,on_delete=models.CASCADE)
photo_type = models.CharField(max_length = 70, db_index = True)
file = models.FileField(upload_to='photos/',default='NoImage.jpg')
cover_photo_flag = models.CharField(default=0,max_length = 5, db_index = True)
uploaded_at = models.DateTimeField(auto_now_add=True)
uploaded_by_id = models.IntegerField(default=0)
status = models.IntegerField(default=0) # 0-->Active,1-->Inactive
class Meta:
ordering = ('-uploaded_at',)
class ProductLikeDislike(models.Model):
product_id = models.ForeignKey(Product,models.SET_DEFAULT,default=0)
product_liked_by_id = models.ForeignKey(User,models.SET_DEFAULT,default=0)
status = models.BooleanField(default=False)
And Please also show me how to write it in pure Django way if possible?
I am getting the same result. Why? Please help me to understand this.
Because .raw(..) [Django-doc] just takes a raw query and executes it. The model from which the raw is performed is irrelevant.
We can generate a query that looks like:
from django.db.models import Q
Product.objects.filter(
Q(photo__photo_flag__isnull=True) | Q(photo__photo_flag='yes'),
Q(likedislike__product_liked_by_id_id=2)
)
So here we accept all Products for which a related Photo object has a flag that is NULL (this also happens in case the JOIN does not yield any flags), or the photo_flag is 'yes'). Furthermore there should be a Likedislike object where the liked_by_id_id is 2.
Note that usually a ForeignKey [Django-doc] has no _id suffix, or id_ prefix. It is also a bit "odd" that you set a default=0 for this, especially since most databases only assign strictly positive values as primary keys, and it makes no sense to inherently prefer 0 over another object anyway.
Something like this:
user_i_care_about = User.objects.get(username='user2')
productlikedislike_set = models.Prefetch('productlikedislike_set',
ProductLikeDislike.objects.select_related('product_liked_by') \
.filter(product_liked_by=user_i_care_about) \
.order_by('id'))
photo_set = models.Prefetch('photo_set', Photo.objects.all()) # this is here incase you need to a select_related()
products = Product.objects.prefetch_related(photo_set, productlikedislike_set) \
.filter(models.Q(photo__cover_photo_flag='yes') | models.Q(photo__isnull=True)) \
.filter(productlikedislike__product_liked_by=user_i_care_about)
Then you can use:
for product in products:
for pic in product.photo_set.all():
print(x.file.name)
# every product here WILL be liked by the user
if your models look something like this:
class Product(models.Model):
# category = models.ForeignKey(Category, on_delete=models.CASCADE) # TODO: uncomment, didnt want to model this out
name = models.CharField(max_length=200, db_index=True)
slug = models.SlugField(max_length=200, db_index=True)
description = models.TextField(blank=True)
price = models.DecimalField(max_digits=10, decimal_places=2) # Not used FloatField to avoid rounding issues # this is correct, no need to explain this, anyonw that works with django, gets this.
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
contact = models.BigIntegerField(default=None,blank=True, null=True)
created_by = models.CharField(max_length=200, default=None, blank=True, null=True)
uploaded_by_id = models.IntegerField(default=0) # TODO: use ForeignKey(User) here!!!
status = models.IntegerField(default=0) # 0-->Active,1-->Inactive # TODO: learn to use `choices`
mark_as_sold = models.IntegerField(default=0) # 0-->not sold,1-->sold # TODO: there is something called `BooleanField` use it!
class Meta:
ordering = ('-created',)
index_together = (('id', 'slug'),) # we want to query product by id and slug using together index to improve performance
def get_absolute_url(self):
return reverse('olx:edit_product', kwargs={'pk': self.pk})
def __str__(self):
return self.name
class Photo(models.Model):
product = models.ForeignKey(Product, null=True,on_delete=models.CASCADE, db_column='reference_id')
photo_type = models.CharField(max_length=70, db_index=True)
file = models.FileField(upload_to='photos/', default='NoImage.jpg')
cover_photo_flag = models.CharField(default=0, max_length=5, db_index=True) # TODO: learn to use `choices`, and you use "yes" / "no" -- and the default is 0 -- FIX THIS!!
uploaded_at = models.DateTimeField(auto_now_add=True)
uploaded_by_id = models.IntegerField(default=0) # TODO: use ForeignKey(User) here!!!
status = models.IntegerField(default=0) # 0-->Active,1-->Inactive # TODO: learn to use `choices` -- perhaps just call this "is_active" and make it a bool
class Meta:
ordering = ('-uploaded_at',)
class ProductLikeDislike(models.Model):
product = models.ForeignKey(Product, models.SET_DEFAULT, default=0) # TODO: default=0?? this is pretty bad. models.ForeignKey(Product, models.SET_NULL, null=True) is much better
product_liked_by = models.ForeignKey(User, models.SET_DEFAULT, default=0, db_column='product_liked_by_id') # TODO: default=0?? this is pretty bad. models.ForeignKey(ForeignKey, models.SET_NULL, null=True) is much better
status = models.BooleanField(default=False) # TODO: rename, bad name. try something like "liked" / "disliked" OR go with IntegerField(choices=((0, 'Liked'), (1, 'Disliked')) if you have more than 2 values.
A full example WITH tests can be seen here: https://gist.github.com/kingbuzzman/05ed095d8f48c3904e217e56235af54a
I have this model in my app which is meant to auto-generate it's primary Key based on a method added in the save().
However, for each object, I will be expected to make updates of certain fields. Right now, anytime I make an update on the admin side (testing use cases) it instead creates a new record of the PK instead of updating the existing one. Any thoughts on how to remedy this?
class DeploymentTask(models.Model):
deployment_id = models.CharField(
'Deployment Task ID', primary_key=True, max_length=25, editable=False)
title = models.CharField(max_length=100)
current_status = FSMField('Current Status',
default=STATES[0], choices=STATES)
site_id = models.ForeignKey(
Site, related_name='+', on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
refuel_record = models.ManyToManyField(RefuelRecord)
def __str__(self):
"""String for representing the Model object."""
return self.deployment_id
class Meta:
db_table = 'rm_deployment_task'
verbose_name_plural = 'Deployment Tasks'
def get_absolute_url(self):
return reverse('deployment_id-view', args=[str(self.deployment_id)])
def save(self):
today = datetime.datetime.now()
ticket_count = DeploymentTask.objects.filter(
created_at__year=today.year, created_at__month=today.month).count() + 1
new_task_id = 'DPT-' + str(str(datetime.date.today().year)) + str(
datetime.date.today().month).zfill(2) + str(
datetime.date.today().day).zfill(2) + '-' + str(ticket_count).zfill(6)
self.deployment_id = new_task_id
super(DeploymentTask, self).save()
enter image description here
You are always setting self.deployment_id to a new value in the save method.
Django tries to do UPDATE ... WHERE deployment_id = %, but there are no records with this id yet (at least if you are saving "old" object on different date or having different ticket_count)
If you want to update fields other than deployment_id, then simply do not set self.deployment_id if it's already set. If you want to update deployment_id then there is not straightforward way to do this, because it's used as primary key (but you can remember old pk and delete that object after you have created a new one during save)
Read more in the Django docs.
This is my updated code, which worked for me...
def make_id():
today = datetime.datetime.now()
ticket_count = DeploymentTask.objects.filter(
created_at__year=today.year, created_at__month=today.month).count() + 1
new_task_id = 'DPT-' + str(str(datetime.date.today().year)) + str(
datetime.date.today().month).zfill(2) + str(
datetime.date.today().day).zfill(2) + '-' + str(ticket_count).zfill(6)
return new_task_id
class DeploymentTask(models.Model):
deployment_id = models.CharField(
'Deployment Task ID', primary_key=True, max_length=25, editable=False, default=make_id)
title = models.CharField(max_length=100)
current_status = FSMField('Current Status',
default=STATES[0], choices=STATES)
site_id = models.ForeignKey(
Site, related_name='+', on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
refuel_record = models.ManyToManyField(RefuelRecord)
def __str__(self):
"""String for representing the Model object."""
return self.deployment_id
class Meta:
db_table = 'rm_deployment_task'
verbose_name_plural = 'Deployment Tasks'
def get_absolute_url(self):
return reverse('deployment_id-view', args=[str(self.deployment_id)])
So I keep getting this error saying that there's multiple specified ID values for the device table, but I don't have a clue where I've specified any kind of default ID. I've tried setting a field as primary_key=True but that didn't solve the problem either.
EDIT: Traceback
class Campus(models.Model):
name = models.CharField(max_length=20)
address = models.CharField(max_length=40)
def __str__(self):
return self.name
class Meta:
verbose_name_plural = "Campuses"
class Teacher(models.Model):
name = models.CharField(max_length=20)
phone = models.CharField(max_length=11)
department = models.CharField(max_length=20)
campus = models.OneToOneField(Campus, on_delete=models.CASCADE, default="Not Assigned")
#devices = self.Device.objects.all()
def __str__(self):
return self.name
class Device(models.Model):
inUse = 'IU'
inStock = 'IS'
inMaintenance = 'IM'
damaged = 'DM'
statusChoices = (
(inUse, 'In Use'),
(inStock, 'In Stock'),
(inMaintenance, 'In Maintenance'),
(damaged, 'Damaged'),
)
name = models.CharField(max_length=20)
brand = models.CharField(max_length=20)
status = models.CharField(max_length=2, choices=statusChoices, default=inStock)
#user = models.ForeignKey(Teacher, on_delete=models.CASCADE, default=0)
def __str__(self):
return self.name
After navigating to my PostgreSQL instance I deleted all Django-related data and remade migrations and things are in working order again.
For future users: I recommend deleting your past migrations table in your database.