Django model field which refers to ImageField of another model - django

I have two models:
class A(models.Model):
title = models.CharField(max_length=100)
a_bg_img = models.ImageField(upload_to='./bg/')
class A_B(models.Model):
title = models.CharField(max_length=100)
b_bg_img = # this should just refer to A's a_bg_img field
b_bg_img should just refer to a_bg_img Field so that I dont save one Image twice. can I just say:
b_bg_img = models.TextField()
and then save there only the link to Image in a_bg_img ?

If you have an image associated with a class A object, then you can easily retrieve the path of that image or url of that image using .path( or get_path()) or .url.
For example:
a = A.objects.get(id=1)
print(a.a_bg_img.path)
'/path/to/image/in/your/local/drive/image.jpg'
print(a.a_bg_img.get_path())
'/path/to/image/in/your/local/drive/image.jpg'
print(a.a_bg_img.url)
'/url/to/your/image'

You can use the django signals post_save to perform actions after the models save()
How ?
create a signals.py file in your app
import it in __init__.py of your main app folder
Inside the signals.py app create function like below:
from django.dispatch import receiver
from django.db.models.signals import post_save
#receiver(post_save, sender=A)
def my_action(sender, instance, **kwargs):
# Your function magic

Related

Cascade delete of model with GenericForeignKey without using GenericRelation

I'm creating a reusable django app which includes a model with GenericForeignKey which I need to be cascade deleted.
This model may be attached to any other. I have no control over target model class as it is outside of the app. This means I can not add GenericRelation field to it and can't force user to add it as target might be in another third-party app.
Assuming we have such models (having NO control over Post and PostGroup):
class Tag(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
object = GenericForeignKey()
class PostGroup(models.Model):
title = models.CharField(max_length=255)
class Post(models.Model):
title = models.CharField(max_length=255)
group = models.ForeignKey(PostGroup, on_delete=models.CASCADE)
Is there a way to delete Tag in case of PostGroup queryset is being deleted?
E.g. not only post_group.delete() but also PostGroup.objects.delete().
You can use pre_delete signal to achieve this:
from django.db.models.signals import pre_delete
from django.dispatch import receiver
#receiver(pre_delete) # add relevant sender to signal (not mandatory)
def post_group_deleted(sender, instance, using, **kwargs):
# Query tags with the instance of PostGroup and delete them
if isinstance(instance, Tag):
return
Tag.objects.filter(
content_type=ContentType.objects.get_for_model(instance),
object_id=instance.pk
).delete()
See documentation here
Unlike ForeignKey, GenericForeignKey does not accept an on_delete argument to customize this behavior; if desired, you can avoid the cascade-deletion by not using GenericRelation, and alternate behavior can be provided via the pre_delete signal.

Django Signals - How to save the instance

I am using Django Rest Framework to upload videos. Now I wanted to add thumbnails to my video files. Whenever a Video object is saved, I wanted to create a thumbnail and set its thumbnail field.
My Video model looks like this:
class Video(models.Model):
created = models.DateTimeField(auto_now_add=True)
text = models.CharField(max_length=100, blank=True)
video = models.FileField(upload_to='Videos/',blank=True)
thumbnail = models.ImageField(upload_to = 'Images/', blank = True)
My signal handler looks like this:
from moviepy.video.io.VideoFileClip import VideoFileClip
from posts.models import Video
from django.db.models.signals import post_save
from django.dispatch import receiver
from settingsFolderOfProject.settings import MEDIA_ROOT
import os
# when save() of Video is done, create_thumbnail_from_video() is called
#receiver(post_save, sender=Video)
def create_thumbnail_from_video(sender, instance, created, **kwargs):
if created:
# create the clip using moviepy's VideoFileClip class
clip = VideoFileClip(os.path.join(MEDIA_ROOT, instance.video.name))
# create the frame at 1st second and set it to instance's thumbnail field
instance.thumbnail = clip.save_frame(os.path.join(MEDIA_ROOT, 'Images/thumbnail.jpg'),t='00:00:01')
# save the instance
instance.save() # <--- I think this op does not work properly
What I have to change?
The thumbnail file gets created in the folder as expected but Django does not set the thumbnail field of my Video model instance. The thumbnail field of the created Video instance is still set to null.
clip.save_frame() doesn't return anything.
Instead do
path = os.path.join(MEDIA_ROOT, 'Images/thumbnail.jpg')
clip.save_frame(path,t='00:00:01')
instance.thumbnail = path
instance.save()
Note: Haven't tested yet. Comment if problem still persists. Thnx...

Django Models make a field that is updating when another field is changing

I want to make a TextField in my model that store every changes of another field. That field to store data like an array. How can I do that? Can anybody give an advice?
exemple:
class Exemple(models.Model):
field = models.ForeignKey(AnotherModel, blank=True)
history_field = models.TextField(blank=True) # => ['old_field_value', 'field_value']
When "field" receive a value I want to append that value to "history_field"
SOLUTION**
pip install django-mysql
from django.db.models.signals import post_save
from django.dispatch import receiver
from django_mysql.models import ListTextField
class Exemple(models.Model):
field = models.ForeignKey(AnotherModel, blank=True)
history_field = ListTextField(
base_field=models.TextField(),
size=150,
blank=True
)
#receiver(post_save, sender=Exemple)
def update_history_field(sender, instance, **kwargs):
if instance.field:
data = Exemple.objects.get(pk=instance.id)
history = data.history_field
history.append(instance.field.name) #field is foreignkey
Exemple.objects.filter(pk=instance.id).update(history_field=history)
#result history_field=['data1', 'data2', 'data3']
This is the solution that I`ve made

Django post_save signal on parent class with multi-table inheritance

In Django, if you have models that use multi-table inheritance, and you define a receiver for a post_save signal on the parent class, does that receiver function get called when an instance of the child class is saved?
Borrowing an example from another question:
class Animal(models.Model):
category = models.CharField(max_length=20)
class Dog(Animal):
color = models.CharField(max_length=10)
def echo_category(sender, **kwargs):
print "category: '%s'" % kwargs['instance'].category
post_save.connect(echo_category, sender=Animal)
If I do:
>>> dog = Dog.objects.get(...)
>>> dog.category = "canine"
>>> dog.save()
Will the echo_category receiver function be called?
post_save.connect(my_handler, ParentClass)
# connect all subclasses of base content item too
for subclass in ParentClass.__subclasses__():
post_save.connect(my_handler, subclass)
have a nice day!
Check out:
https://code.djangoproject.com/ticket/9318
It appears that most propagate the signal to the super in the subclass.
No, it will not be called. See #9318 in Django trac.
I managed to get inherited signal receivers working with the #receiver decorator. See relevant Django documentation
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
class Animal(models.Model):
category = models.CharField(max_length=20)
#receiver(post_save)
def echo_category(sender, **kwargs):
print ("category: '%s'" % kwargs['instance'].category)
class Dog(Animal):
color = models.CharField(max_length=10)
This solution is valid in Python 3.6.8 Django 2.2
When I do this
>>> from myapp.models import Dog
>>> dog = Dog()
>>> dog.category = "canine"
>>> dog.save()
category: 'canine'
>>>
No problems. Everything seems to work from the shell.
Slightly unrelated, but when I edited models through the admin panel There was an issue with it getting called twice so I filtered them by checking the 'created' kwarg. In one call it was false, the other it was true so I just put in a simple if block.
Credit for that workaround goes to Pratik Mandrekar and his answer:
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
class Animal(models.Model):
category = models.CharField(max_length=20)
#receiver(post_save)
def echo_category(sender, **kwargs):
if not kwargs.get('created'):
print ("category: '%s'" % kwargs['instance'].category)
class Dog(Animal):
color = models.CharField(max_length=10)

Django-photologue ImageModel

This is my first project in django and im using photologue for gallery, which is awesome and i really like it.
But there is one thing i dont get, how can i use its ImageModel?
I have a blog-app and with every new blogpost created in the admin interface i woud like to upload a image which is linked to that post.
from django.db import models
from tagging.fields import TagField
from tinymce import models as tinymce_models
from photologue.models import ImageModel
#import datetime
# Create your models here.
class Blog(models.Model):
title = models.CharField(max_length=150)
content = tinymce_models.HTMLField()
pub_date = models.DateTimeField(auto_now_add=True)
edit_date = models.DateTimeField(auto_now=True)
tags = TagField()
summary = models.CharField(max_length=30)
thumbnail = ImageModel()
def __unicode__(self):
return self.title
This code above doesn't seem to work or do anything in fact.
I have been rifling through the docs, google ect and trying to understand the photologue source myself but i cant seem to get it to work like i want to.
ImageModel is an abstract class. You can't use it as itself. Instead, you must subclass it:
class BlogImage(ImageModel):
pass
class Blog(models.Model):
...
thumbnail = models.ForeignKey(BlogImage, related_name='blogs')
But, the main purpose of ImageModel is to allow you to create a photo model with additional custom data that still behaves like one of photologue's models. photologue already has Photo which is a real model based on ImageModel that you can use if you just need the defaults.
class Blog(models.Model):
...
thumbnail = models.ForeignKey(Photo, related_name='blogs')