How can a django model field relate to multiple model? - python

suppose I have three model like this:
class video(models.Model):
name=models.CharField(max_length = 100)
class image(models.Model):
name=models.CharField(max_length = 100)
class comments(models.Model):
content=models.CharField(max_length = 100)
now i want to notic the user if their video or image get an comment
this is what i want
the message model:
class message(models.Model):
type=models.CharField(max_length = 100) # 'video' or 'image'
video_or_image=models.ForeignKey(video or image)
#the type is just a string to tell if the comment is about the video or image
#video_or_image need to be video foreignkey or image foreignkey depends on type
is it possible.
I currently work around this by two method
first:
class message(models.Model):
type = models.CharField(max_length = 100) # 'video' or 'image'
video_or_image_id = models.IntegerField(default = 1)
#
second
class message(models.Model):
type=models.CharField(max_length = 100) # 'video' or 'image'
video=models.ForeignKey(video)
image=models.ForeignKey(image)
# if the comment is about video just leave the image empty
if the one field to multiple model can not be done, then which my work around method is better, or help me with a better one!

You are looking for a GenericForeignKey.
This is also the way contrib.comments relates comments to commented items.

Related

Create multiple entries for one model in a Django modelform

Sorry if the title is confusing, I can't really think of how else to word it.
I am creating a site where there are many quizzes. Each Quiz model
class Quiz(models.Model):
name = models.CharField(max_length = 80)
description = models.CharField(max_length = 300)
num_questions = models.IntegerField(default = 10)
slug = models.SlugField(max_length = 20)
img = models.URLField(blank = True) # allow it to be none
def __str__(self):
return self.name
def get_questions(self):
return self.question_set.all()
looks like this... and it has some attributes like name, description, etc. There are many Question models that have ForeignKey to one Quiz:
class Question(models.Model):
quiz = models.ForeignKey(Quiz, on_delete = models.CASCADE)
img = models.URLField(blank = True) # allow none`
content = models.CharField(max_length = 200)
def __str__(self):
return self.content
def get_answers(self):
return self.answer_set.all()
and then there are some Choice models that have ForeignKey to one Question:
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete = models.CASCADE)
content = models.CharField(max_length = 200)
correct = models.BooleanField(default = False)
Now. I want to create one single ModelForm from which I can create 1 quiz record, and then 10 question records with 4 choice records per question. It would be very nice if they could automatically set their foreignkey to the Question that is being created. How can I go about this? Is it even possible? I don't even know if my wording of this question is making sense because I have a great big idea in my head but no idea how to express it properly in words or code.
Help is appreciated :)
If you have 3 models and you want to show them in a single form in your HTML file. Then you can simply create a model form for each of them and add them in a single <form> tag.
The answer to a similar question is posted here.
If you mean inserting multiple records at the same time, then consider looking at formset factory - https://docs.djangoproject.com/en/3.2/topics/forms/formsets/

Resize image according to other model fields

How do I use the value of other model fields in some field? I want to resize an image to the width and height specified in their respective fields:
class MyImage(models.Model):
name = models.CharField(max_length=128, blank=False)
width = models.PositiveIntegerField(blank=False) #--->
height = models.PositiveIntegerField(blank=False) #--->
new_image = ProcessedImageField(processors=[ResizeToFill(500, 500)], #<---
format='JPEG',
options={'quality': 60})
I have to to swap first 500 into value from variable width and and second 500 into variable height. The field ProcessedImageField is from a package django-imagekit that I am using.
You can create an exchange function, where through ORM you take all your objects and walk through them exchanging the necessary values
Django ORM query: how to swap value of a attribute?
As described in the django-imagekit documentation on Specs That Change you can write a separate class for the ImageSpec provide the processors attribute as a property to dynamically get the proccessors, register this image spec and provide it's id to your ProcessedImageField to achieve your purpose:
from django.db import models
from imagekit import ImageSpec, register
from imagekit.models import ProcessedImageField
from imagekit.processors import ResizeToFill
from imagekit.utils import get_field_info
class YourImage(ImageSpec):
format = 'JPEG'
options = {'quality': 60}
#property
def processors(self):
model, field_name = get_field_info(self.source)
return [ResizeToFill(model.width, model.height)]
register.generator('your_app:your_image', YourImage)
class MyImage(models.Model):
name = models.CharField(max_length=128, blank=False)
width = models.PositiveIntegerField(blank=False)
height = models.PositiveIntegerField(blank=False)
new_image = ProcessedImageField(spec_id='your_app:your_image')

Sorting images by height/orientation

I made image gallery with grid view, but I don't like the way that rows look like - vertical photos disrupt everything. As I don't want to manually change images order I'm looking for a way to sort them automaticaly by image height or just image orientation, so vertical photos go to the bottom in one row.
Thats how my model in Django looks like:
class Photo(models.Model):
title = models.CharField(max_length=150)
image = models.ImageField()
description = models.TextField(blank=True)
category = models.IntegerField(choices=CATEGORIES)
published = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.title
Here is my grid_view:
def photos_grid(request):
global cat_list
photos = Photo.objects.order_by('published')
output = {'photos': photos, 'categories': cat_list,}
return render(request, 'photos/photos_grid.html', output)
I tried that (how to find height and width of image for FileField Django) way of getting image dimensions but I got
ValueError: invalid literal for int() with base 10: 'height'
every way I try to put it in my code. Other idea (by getting manually dimensions in views.py) works, but I can't put it together with my photos on the list so it get sorted.
You must include the height and width fields in your model, for example:
class Photo(models.Model):
image = models.ImageField(height_field='image_height', width_field='image_width')
image_height = models.IntegerField()
image_width = models.IntegerField()
...
After migrating your database, you can write the following:
Photo.objects.all().order_by('image_height')
Edit: If you need to access the orientation, add another field, such as:
class Photo(models.Model):
...
aspect_ratio = models.FloatField(blank=True, null=True)
then, override your save method to populate this field using height and width, i.e.:
class Photo(models.Model):
...
def save(self, **kwargs):
self.aspect_ratio = float(self.image_height) / float(self.image_width)
super(Photo, self).save(kwargs)
You can then order by your new field, such as:
Photo.objects.all().order_by('aspect_ratio')

Store list of images in django model

I am building a Django data model and I want to be able to store an array of ImageFields.
Is it possible?
mainimage = models.ImageField(upload_to='img', null = True)
images = models.??
Thanks.
Create another model to images and have foreignkey with your model.
def YourModel(models.Model):
#your fields
def ImageModel(models.Model):
mainimage = models.ImageField(upload_to='img', null = True)
image = models.ForeignKey(YourModel, ...)
I would use the ManyToMany relationship to link your model with an image model. This is the way to aggregate ImageField as django does not have aggregate model field
def YourModel(models.Model):
images = ManyToManyField(ImageModel)
...
def ImageModel(models.Model):
img = ImageField()
name ...
Maybe you need something more performant (this could lead to lots of horrible joins)

Recursive Relationship with a Description in Django Models

My project involves sorting many images. As part of this sorting, I want to be able to manually (as the user) mark several images as duplicates of each other with a brief description of why each relationship was created. These relationships will not be defined at the time an image is loaded into Django, but at a later time after uploading all the images.
My question: How can I create an unlimited number of duplicates? Aka, how would I define that several images are all related to each other, and include a CharField description of why each relationship exists?
This is a django app and the code is from models.py.
Thank you.
from django.db import models
class tag(models.Model):
tag = models.CharField(max_length=60)
x = models.IntegerField(null=True)
y = models.IntegerField(null=True)
point = [x,y]
def __unicode__(self):
return self.tag
#...
class image(models.Model):
image = models.ImageField(upload_to='directory/')
title = models.CharField(max_length=60, blank=True, help_text="Descriptive image title")
tags = models.ManyToManyField(tag, blank=True, help_text="Searchable Keywords")
#...
##### HELP NEEDED HERE ##################
duplicates = [models.ManyToManyField('self', null=True), models.CharField(max_length=60)]
##########################################
def __unicode__(self):
return self.image.name
You'd have to go with an extra model for grouping those duplicates, because you want a description field with it. Something like
class DupeSeries(Model):
description = CharField(...)
members = ManyToManyField("image", related_name="dupes", ...)
Example usage:
img = image(title="foo!", image="/path/to/image.jpg")
dup_of_img = image(title="foo!dup", image="/path/to/dup/image.jpg")
img.save()
dup_of_img.save()
dupes_of_foo = DupeSeries(description="foo! lookalikes")
dupes_of_foo.members.add(img, dup_of_img)
# Notice how *img.dupes.all()* returns both image instances.
assert(list(img.dupes.all()) == [img, dup_of_img])

Categories

Resources