I've added photologue to my project, because it does 99% of the things I was trying to achieve by myself.
Now I need to connect the uploaded images and images in galleries to my posts. This is why I've added a ManyToMany field to the photologue Photo model
Class ImageModel(models.Model):
post_images = models.ManyToManyField(Post, blank=True)
And will do the same to the Gallery model
class Gallery(models.Model):
post_gallery = models.ManyToManyField(Post, blank=True)
Now my idea is to be able to add a gallery and/or specific images to my post . Both options should be available.
The queryset I would like to have is to get all the individual images, that are attached to the post and also all the images in the gallery if a gallery is attached. Then to pass and render them to the page in some sort of gallery (Slider, carousel or something else).
I'd like to be able to get them in the template with one for loop, so I think the queryset have to be one.
I have a view that renders the specific page type and I don't know if I should include the queryset and context in this function or to create a new one for the images. And how to do it.
def post(request, slug):
post = get_object_or_404(Post, post_slug=slug)
context = {
'post': post,
}
return render(request, 'project_name/post.html', context)
I hope I have explained what I'd like to do. Django is new to me and querysets are still a bit complex.
Shouldn't you try adding galleriesto posts from posts?
from photologue.models import Gallery
class Post(models.Model):
gallery = models.ManyToManyField(Gallery, blank=True )
#Edit ___
#TwinDewey In this case you need to make a query in Views to get the Galleries when showing Post details.
galleries = gallery.posts.all()
gal = []
for excercise in excercises:
gal.append(Gallery.objects.filter(gallery__name=self.post.title))
Or you can make a query and merge it with the Post context using
from itertools import chain
list(chain(post_context,Gallery.objects.filter(gallery__name=self.post.title)))
That´s for gallery. Posts would have another query.
You can use union to group the photos tied to the post itself and inside the gallery. The trick is to get the right models so they are the same objects:
from django.db import models
from photologue.models import Gallery, Photo
class Post(models.Model):
title = models.CharField(max_length=100)
published_at = models.DateTimeField(blank=True, null=True)
text = models.TextField()
photos = models.ManyToManyField(Photo, related_name='posts')
galleries = models.ManyToManyField(Gallery, related_name='posts')
#property
def all_images(self):
# Create a number of querysets that have photos from selected galleries
# ..note:
# .only() is used to trim down on the fields fetched from the database.
# .prefetch_related() is used to eliminate extra queries during iteration
qs = []
for gallery in self.galleries.prefetch_related('photos').only('photos'): # type: Gallery
qs.append(gallery.photos.only('image', 'title'))
return self.photos.only('image', 'title').union(*qs)
Now you can use it in a template like so:
{% for photo in post.all_images %}
<img src="{{ photo.image.url }}" alt="{{ photo.title }}" />
{% endfor %}
Related
I'm making a picture gallery web-app. I want to make some of displayed photos to belong to a specific collection. Each collection is supposed to have its own page that displays all of the pictures that belong to it.
The name of each unique page is supposed to be photo_collection model, which I added to the class Image in models.py. But for some reason, I get TypeError at /projects/sample_collection_name/ - photo_collection() got an unexpected keyword argument 'photo_collection'
No idea what's causing this error. I tried renaming the photo_collection function (it has the same name as my model), but it didn't work.
models.py
class Image(models.Model):
title = models.CharField(max_length=300)
image = models.ImageField(null=True, blank=True, upload_to='images/')
pub_date = models.DateTimeField('Date published', default=timezone.now)
photo_collection = models.CharField('Photo collection', max_length=250, null=True, blank=True)
views.py
def photo_collection(request):
image = Image.objects.all()
return render (request, 'photo_app/collection.html', context={'image': image})
urls.py
urlpatterns = [
#some other patterns here
path('projects/<str:photo_collection>/', views.photo_collection, name='photo_collection'),
]
gallery.html
{% if i.photo_collection != Null %}
{{i.photo_collection}}
{% endif %}
you need to add photo_collection to your view parameters.
it will be like this:
def photo_collection(request, photo_collection):
image = Image.objects.all()
return render (request, 'photo_app/collection.html', context={'image': image})
when ever you add a variable to your url path you need to add that variable to view parameters too.
here is the documentation for this matter:
https://docs.djangoproject.com/en/4.1/topics/http/urls/
I am running into an issue where my database is information is displaying on one template, but I want certain parts to display on another page for a blog.
When I click into physics-blog it will display my images and post title. For this, I have looped through the database. Works fine and perfectly.
But when I click into one of them and want to show {{ physicsBlog.body }} it doesn't show anything. Which I can't wrap my head around because that works just fine in the other ListView template, but not in the DetailView template.
Here is my code.
models.py
class physicsBlog(models.Model):
title = models.CharField(max_length=250)
blog_image = models.ImageField(null=True, blank=True)
description = models.CharField(max_length=200)
author = models.ForeignKey(User, on_delete=models.CASCADE)
body = RichTextField(blank=True, null=True)
date_created = models.DateField(auto_now_add=True)
def __str__(self):
return self.title + ' | ' + str(self.author)
views.py
class physicsBlogListView(ListView):
model = physicsBlog
template_name = 'physics.html'
ordering = ['-id']
class physicsBlogDetailView(DetailView):
model = physicsBlog
template_name = 'physics-blog-details.html'
urls.py
urlpatterns = [
path('', views.home, name="home"),
path('physics-blog', physicsBlogListView.as_view(), name="physics-blog"),
path('physics-blog/<int:pk>', physicsBlogDetailView.as_view(), name="physics-blog-details"),
path('crypto-blog', cryptoBlogListView.as_view(), name="crypto-blog"),
path('crypto-blog/<int:pk>', cryptoBlogDetailView.as_view(), name="crypto-blog-details"),
]
I'm not super familiar using these generic displays that Django provides, but from the django docs it says
While this view is executing, self.object will contain the object that
the view is operating upon.
So maybe try {{ object.body }} in your template?
You can use standard way of rendering data in standard single-object view (mostly DetailView):
{{ object.body }}
But if you want to see it better, just add context_object_name variable:
class physicsBlogDetailView(DetailView):
context_object_name = "physics_blog"
...
After such change in template:
{{ physics_blog.body }}
Without context_object_name it's object or object_list. I'm kinda surprised that you did ok in ListView and had problem in DetailView, because it's the same thing.
I'm struggling getting the right query for my project. Here is an example or my model :
from django.db import models
class Pictures(models.Model):
name = models.CharField(max_length=100)
bild = models.FileField(upload_to='article_pictures/')
articel = models.ForeignKey('articles', on_delete=models.CASCADE)
def __str__(self):
return self.name
class Articles(models.Model):
name = models.CharField(max_length=100)
text = models.TextField(max_length=2000)
published = models.BooleanField(default=False)
def __str__(self):
return self.name
how do I get the published artikles from the artikles class including the pictures (if there is one, or more)?
Thank you for your help
I don't think there is any exact query for this, but you can use prefetch_related to pre-load data from database. For example:
articles = Artikles.objects.filter(published=True).prefetch_related('pictures_set')
for article in articles:
article.pictures_set.all() # will not hit database
All published articles:
Articles.objects.filter(published=True)
A single published Article(Example):
article = Articles.objects.filter(published=True).first()
# and it's pictures
for picture in article.pictures_set.all():
print(picture)
Note: models have singular names, so you should rename Articles to Article and Pictures to Picture.
The related Pictures of an article article can be obtained with:
my_article.picture_set.all()
this is a queryset that contains all the related pictures.
We can obtain the Articles that are publised, and then fetch the related Pictures in two extra queries with:
articles = Article.objects.filter(published=True).prefetch_related('picture_set')
So in a template you can then render it like:
{% for article in articles %}
{{ article.name }}
{% for picture in article.picture_set.all %}
{{ picture.name }}
{% endfor %}
{% endfor %}
I have three different models for my app. All are working as I expected.
class Tender(models.Model):
title = models.CharField(max_length=256)
description = models.TextField()
department = models.CharField(max_length=50)
address = models.CharField(max_length=50)
nature_of_work = models.CharField(choices=WORK_NATURE, max_length=1)
period_of_completion = models.DateField()
pubdat = models.DateTimeField(default=timezone.now)
class Job(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
title = models.CharField(max_length=256)
qualification = models.CharField(max_length=256)
interview_type = models.CharField(max_length=2, choices=INTERVIEW_TYPE)
type_of_job = models.CharField(max_length=1, choices=JOB_TYPE)
number_of_vacancies = models.IntegerField()
employer = models.CharField(max_length=50)
salary = models.IntegerField()
pubdat = models.DateTimeField(default=timezone.now)
class News(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
title = models.CharField(max_length=150)
body = models.TextField()
pubdat = models.DateTimeField(default=timezone.now)
Now I am displaying each of them at separate page for each of the model (e.g. in the jobs page, I am displaying only the jobs.). But now at the home page, I want to display these according to their published date at the same page. How can I display different objects from different models at the same page? Do I make a separate model e.g. class Post and then use signal to create a new post whenever a new object is created from Tender, or Job, or News? I really hope there is a better way to achieve this. Or do I use multi-table inheritance? Please help me. Thank you.
Update:
I don't want to show each of the model objects separately at the same page. But like feeds of facebook or any other social media. Suppose in fb, any post (be it an image, status, share) are all displayed together within the home page. Likewise in my case, suppose a new Job object was created, and after that a new News object is created. Then, I want to show the News object first, and then the Job object, and so on.
A working solution
There are two working solutions two other answers. Both those involve three queries. And you are querying the entire table with .all(). The results of these queries combined together into a single list. If each of your tables has about 10k records, this is going to put enormous strain on both your wsgi server and your database. Even if each table has only 100 records each, you are needlessly looping 300 times in your view. In short slow response.
An efficient working solution.
Multi table inheritance is definitely the right way to go if you want a solution that is efficient. Your models might look like this:
class Post(models.Model):
title = models.CharField(max_length=256)
description = models.TextField()
pubdat = models.DateTimeField(default=timezone.now, db_index = True)
class Tender(Post):
department = models.CharField(max_length=50)
address = models.CharField(max_length=50)
nature_of_work = models.CharField(choices=WORK_NATURE, max_length=1)
period_of_completion = models.DateField()
class Job(Post):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
qualification = models.CharField(max_length=256)
interview_type = models.CharField(max_length=2, choices=INTERVIEW_TYPE)
type_of_job = models.CharField(max_length=1, choices=JOB_TYPE)
number_of_vacancies = models.IntegerField()
employer = models.CharField(max_length=50)
salary = models.IntegerField()
class News(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
def _get_body(self):
return self.description
body = property(_get_body)
now your query is simply
Post.objects.select_related(
'job','tender','news').all().order_by('-pubdat') # you really should slice
The pubdat field is now indexed (refer the new Post model I posted). That makes the query really fast. There is no iteration through all the records in python.
How do you find out which is which in the template? With something like this.
{% if post.tender %}
{% else %}
{% if post.news %}
{% else %}
{% else %}
Further Optimization
There is some room in your design to normalize the database. For example it's likely that the same company may post multiple jobs or tenders. As such a company model might come in usefull.
An even more efficient solution.
How about one without multi table inheritance or multiple database queries? How about a solution where you could even eliminate the overhead of rendering each individual item?
That comes with the courtesy of redis sorted sets. Each time you save a Post, Job or News, object you add it to a redis sorted set.
from django.db.models.signals import pre_delete, post_save
from django.forms.models import model_to_dict
#receiver(post_save, sender=News)
#receiver(post_save, sender=Post)
#receiver(post_save, sender=Job)
def add_to_redis(sender, instance, **kwargs):
rdb = redis.Redis()
#instead of adding the instance, you can consider adding the
#rendered HTML, that ought to save you a few more CPU cycles.
rdb.zadd(key, instance.pubdat, model_to_dict(instance)
if (rdb.zcard > 100) : # choose a suitable number
rdb.zremrangebyrank(key, 0, 100)
Similarly, you need to add a pre_delete to remove them from redis
The clear advantage of this method is that you don't need any database queries at all and your models continue to be really simple + you get catching thrown in the mix. If you are on twitter your timeline is probably generated through a mechanism similar to this.
The following should do want you need. But to improve performance you can create an extra type field in each of your models so the annotation can be avoided.
Your view will look something like:
from django.db.models import CharField
def home(request):
# annotate a type for each model to be used in the template
tenders = Tender.object.all().annotate(type=Value('tender', CharField()))
jobs = Job.object.all().annotate(type=Value('job', CharField()))
news = News.object.all().annotate(type=Value('news', CharField()))
all_items = list(tenders) + list(jobs) + list(news)
# all items sorted by publication date. Most recent first
all_items_feed = sorted(all_items, key=lambda obj: obj.pubdat)
return render(request, 'home.html', {'all_items_feed': all_items_feed})
In your template, items come in the order they were sorted (by recency), and you can apply the appropriate html and styling for each item by distinguishing with the item type:
# home.html
{% for item in all_items_feed %}
{% if item.type == 'tender' %}
{% comment "html block for tender items" %}{% endcomment %}
{% elif item.type == 'news' %}
{% comment "html block for news items" %}{% endcomment %}
{% else %}
{% comment "html block for job items" %}{% endcomment %}
{% endif %}
{% endfor %}
You may avoid the annotation altogether by using the __class__ attribute of the model objects to distinguish and put them in the appropriate html block.
For a Tender object, item.__class__ will be app_name.models.Tender where app_name is the name of the Django application containing the model.
So without using annotations in your home view, your template will look:
{% for item in all_items_feed %}
{% if item.__class__ == 'app_name.models.Tender' %}
{% elif item.__class__ == 'app_name.models.News' %}
...
{% endif %}
{% endfor %}
With this, you save extra overhead on the annotations or having to modify your models.
A straight forward way is to use chain in combination with sorted:
View
# your_app/views.py
from django.shortcuts import render
from itertools import chain
from models import Tender, Job, News
def feed(request):
object_list = sorted(chain(
Tender.objects.all(),
Job.objects.all(),
News.objects.all()
), key=lambda obj: obj.pubdat)
return render(request, 'feed.html', {'feed': object_list})
Please note - the querysets mentioned above using .all() should be understood as placeholder. As with a lot of entries this could be a performance issue. The example code would evaluate the querysets first and then sort them. Up to some hundreds of records it likely will not have a (measurable) impact on performance - but in a situation with millions/billions of entries it is worth looking at.
To take a slice before sorting use something like:
Tender.objects.all()[:20]
or use a custom Manager for your models to off-load the logic.
class JobManager(models.Manager):
def featured(self):
return self.get_query_set().filter(featured=True)
Then you can use something like:
Job.objects.featured()
Template
If you need additional logic depending the object class, create a simple template tag:
#templatetags/ctype_tags.py
from django import template
register = template.Library()
#register.filter
def ctype(value):
return value.__class__.__name__.lower()
and
#templates/feed.html
{% load ctype_tags %}
<div>
{% for item in feed reversed %}
<p>{{ item|ctype }} - {{ item.title }} - {{ item.pubdat }}</p>
{% endfor %}
</div>
Bonus - combine objects with different field names
Sometimes it can be required to create these kind of feeds with existing/3rd party models. In that case you don't have the same fieldname for all models to sort by.
DATE_FIELD_MAPPING = {
Tender: 'pubdat',
Job: 'publish_date',
News: 'created',
}
def date_key_mapping(obj):
return getattr(obj, DATE_FIELD_MAPPING[type(obj)])
def feed(request):
object_list = sorted(chain(
Tender.objects.all(),
Job.objects.all(),
News.objects.all()
), key=date_key_mapping)
Do I make a separate model e.g. class Post and then use signal to
create a new post whenever a new object is created from Tender, or
Job, or News? I really hope there is a better way to achieve this. Or
do I use multi-table inheritance?
I don't want to show each of the model objects separately at the same
page. But like feeds of facebook or any other social media.
I personally don't see anything wrong using another model, IMHO its even preferable to use another model, specially when there is an app for that.
Why? Because I would never want to rewrite code for something which can be achieved by extending my current code. You are over-engineering this problem, and if not now, you are gonna suffer later.
An alternative solution would be to use Django haystack:
http://haystacksearch.org/
http://django-haystack.readthedocs.io/en/v2.4.1/
It allows you to search through unrelated models. It's more work than the other solutions but it's efficient (1 fast query) and you'll be able to easily filter your listing too.
In your case, you will want to define pubdate in all the search indexes.
I cannot test it right now, but you should create a model like:
class Post(models.Model):
pubdat = models.DateTimeField(default=timezone.now)
tender = models.ForeignKey('Tender')
job = models.ForeignKey('Job')
news = models.ForeignKey('News')
Then, each time a new model is created, you create a Post as well and relate it to the Tender/Job/News. You should relate each post to only one of the three models.
Create a serializer for Post with indented serializers for Tender, Job and News.
Sorry for the short answer. If you think it can work for your problem, i'll write more later.
sorry if this is an obvious question but I have been searching for a few days and have not been able to come up with a result.
I am creating a simple photo gallery app. There are four galleries, each containing a photo (the photo consists of a 'before' image, 'after' image and caption). I am trying to use django-admin to allow users to click on a gallery and then add photos.
I am using a TabularInline to edit the photos within each gallery. In addition to the default columns on the TabularInline, I would like to add a column that shows a thumbnail preview of the 'before' photo and 'after' photo (I am using easy-thumbnails for this). After much searching, it seems like the best way to do this is to override the django-admin tabularInline.html template and add the column myself - so I created another copy and am trying to edit it now.
What I would like to do is simply reference the Photo object within the Django admin template that I am overriding - but I don't know the appropriate tag to use. I need the reference so I can use it in conjunction with the easy-thumbnails thumbnail tag ... but for the life of me I cannot figure out the template tag that references the object. I have tried iterating through the ModelForm, FormSet, and FieldSet objects but none seem to give me a direct reference to the object.
# models.py
class Gallery(models.Model):
name = models.CharField(max_length=200)
url = models.CharField(max_length=200)
desc = models.TextField()
def __unicode__(self):
return self.name
class Photo(models.Model):
gallery = models.ForeignKey(Gallery)
before = models.ImageField(upload_to='gallery')
after = models.ImageField(upload_to='gallery')
caption = models.CharField(max_length=1000)
order = models.IntegerField(blank = True, null = True)
def __unicode__(self):
return "Photo " + str(self.order)
# admin.py
class GalleryForm(forms.ModelForm):
model = Gallery
class Media:
js = (
'/assets/js/jquery-1.4.2.min.js',
'/assets/js/jquery-ui-1.8.2.custom.min-admin-sortable.js',
'/assets/js/menu-sort.js',
)
class PhotoInline(admin.TabularInline):
model = Photo
extra = 1
template = "admin/tabular-thumbnails.html"
admin.site.register(Gallery,
inlines=[PhotoInline],
form = GalleryForm)
Thanks so much in advance and please let me know if there's any additional information I can offer. I am using Django 1.1
In Django 2.1 {{adminform.form.instance.field_name}} worked for me.
{{ form.instance }} will always be the model instance associated with a modelform, assuming there is one.
(Note that ``{{ formset.instance }}` is the instance of the parent model in an inline formset).