显示存储在Django数据库中的图像。

huangapple go评论139阅读模式
英文:

Displaying an image stored in a Django database

问题

I have a website dedicated to pets (dead ones), and I have some stock images in the database stored under the pet using a many-to-many field (so, any number of pets can be in an image, and a pet can have any number of images associated with it--seems logical so far). I am trying to display the pet photos in any way at all in one of the templates.

My problem is, I have no idea of the correct syntax for iterating over a `context_dictionary data structure I have made to store pets and pet photos for the purpose of accessing in the associated template file. I am having to do this because I think (correct me if I'm wrong) a view can only have one associated model, and I need a few models, their data, to display in one view. So. It is what it is.

I think it's probably about time I showed some of my code. Here is one after another:

pet_profile/models.py

from django.db import models
from django.utils.crypto import get_random_string
import uuid
from django.urls import reverse

class PetPhoto(models.Model):
    # Fields for PetPhoto model
    # ...

class PetStory(models.Model):
    # Fields for PetStory model
    # ...

ANIMALTYPE_CHOICES = (
    ('dog', 'the one that barks'),
    ('cat', 'the one that meows'),
    ('lizard', 'the one that eats crickets'),
    ('snake', 'the one that slithers'),
    ('rabbit/bunny', 'the one that hops'),
    ('bird', 'the one that flaps'),
    ('fish', 'the one that swims'),
    ('frog', 'the one that croaks'),
)

class Pet(models.Model):
    # Fields for Pet model
    # ...

class PetOwner(models.Model):
    # Fields for PetOwner model
    # ...

def slug_save(obj):
    """ A function to generate a 5-character slug and check if it has been used and contains naughty words."""
    # ...

def get_ID(obj):
    obj.id = obj.name + '-' + obj.slug

def get_ID2(obj):
    obj.id = obj.title + '-' + obj.slug

pet_profile/views.py

from django.shortcuts import render
from django.views.generic import (ListView, DetailView)
from pet_profile.models import PetOwner, Pet, PetPhoto, PetStory

class PetOwnerListView(ListView):
    # ...

class PetOwnerDetailView(DetailView):
    # ...

class PetListView(ListView):
    # ...

class PetDetailView(DetailView):
    # ...

class PetPhotoListView(ListView):
    # ...

class PetPhotoDetailView(DetailView):
    # ...

class PetStoryListView(ListView):
    # ...

class PetStoryDetailView(DetailView):
    # ...

pet_owner_profile.html

{% extends "base.html" %}
{% block header %}
{% endblock %}
{% block content %}
    {{ owner.name }}
    {{ owner.age }}
    {{ owner.location }}
    {{ owner.profile_photo }}
    {% for pet in owner.pets.all %}
        {{ pet.name }}
        {{ pet.animaltype }}
        {{ pet.age }}
        <!-- context['pet_photos'] = {pet1: [photo1, photo2, photo3], pet2: [photo1, photo2]} -->
        {% for photo in pet_photos.pet.photos %}
            <img src="{{ photo.photo.url }}">
        {% endfor %}
    {% endfor %}
{% endblock %}

In your code, you have defined models for Pet, PetPhoto, PetStory, and PetOwner. You also have views for listing and displaying these models. However, there seem to be issues with your code:

  1. In the PetOwnerDetailView view, you're trying to iterate over pet_photos.pet.photos, which is not the correct way to access photos associated with pets. You should directly iterate over pet.pet_photos.all to access the related photos of each pet.

  2. In your models, you have used HTML entities like &quot; and &amp; for double quotes and ampersands. You should use plain double quotes (") and ampersands (&) in your code.

  3. You've defined the ANIMALTYPE_CHOICES using double quotes, which should be single quotes.

  4. Your static file settings (STATIC_URL and STATIC_ROOT) appear to be correctly set up.

Make sure to address these issues in your code for it to work as intended.

英文:

I have a website dedicated to pets (dead ones), and I have some stock images in the database stored under the pet using a manytomany field (so, any number of pets can be in an image, and a pet can have any number of images associated with it--seems logical so far). I am trying to display the pet photos in any way at all in one of the templates.

My problem is, I have no idea of the correct syntax for iterating over a `context_dictionary data structure I have made to store pets and pet photos for the purpose of accessing in the associated template file. I am having to do this because I think (correct me if I'm wrong) a view can only have one associated model, and I need a few models, their data, to display in one view. So. It is what it is.

I think it's probably about time I showed some of my code. Here is one after another:

pet_profile/models.py

from django.db import models
from django.utils.crypto import get_random_string
import uuid
from django.urls import reverse

class PetPhoto(models.Model):
    slug = models.SlugField(max_length = 5, primary_key = True, blank = True, null=False)
    title = models.CharField(max_length = 255)
    id = models.CharField(max_length = 261, default=uuid.uuid1)
    photo = models.ImageField(blank = False)

    def save(self, *args, **kwargs):  # new
        slug_save(self)
        get_ID2(self)
        return super().save(*args, **kwargs)

    def __str__(self):
        return self.title


class PetStory(models.Model):
    slug = models.SlugField(max_length = 5, primary_key = True, blank = True, null=False)
    title = models.CharField(max_length = 255)
    content = models.TextField(max_length = 1000)
    
    def save(self, *args, **kwargs):  # new
        slug_save(self)
        get_ID(self)
        return super().save(*args, **kwargs)

    def __str__(self):
        return self.title

    class Meta:
        verbose_name_plural = &quot;PetStories&quot;


ANIMALTYPE_CHOICES = (
    (&#39;dog&#39;, &#39;the one that barks&#39;),
    (&#39;cat&#39;, &#39;the one that meows&#39;),
    (&#39;lizard&#39;, &#39;the one that eats crickets&#39;),
    (&#39;snake&#39;, &#39;the one that slithers&#39;),
    (&#39;rabbit/bunny&#39;, &#39;the one that hops&#39;),
    (&#39;bird&#39;, &#39;the one that flaps&#39;),
    (&#39;fish&#39;, &#39;the one that swims&#39;),
    (&#39;frog&#39;, &#39;the one that croaks&#39;),
)

class Pet(models.Model):
    slug = models.SlugField(max_length = 5, primary_key = True, blank = True, null=False)
    name = models.CharField(max_length = 255, unique = False)
    id = models.CharField(max_length = 261, unique = True, default = uuid.uuid1)
    animaltype = models.CharField(choices = ANIMALTYPE_CHOICES, max_length = 255, default=&quot;the one that barks&quot;)
    age = models.PositiveIntegerField()
    pet_photos = models.ManyToManyField(PetPhoto, related_name = &quot;pets&quot;, blank = True)
    pet_stories = models.ManyToManyField(PetStory, related_name = &quot;pets&quot;, blank = True)

    def save(self, *args, **kwargs):  # new
        slug_save(self)
        get_ID(self)
        return super().save(*args, **kwargs)

    def __str__(self):
        return self.name


class PetOwner(models.Model):
    slug = models.SlugField(max_length = 5, primary_key = True, blank = True, null=False)
    name = models.CharField(max_length = 255)
    id = models.CharField(max_length = 261, unique = True, default=uuid.uuid1)
    age = models.PositiveIntegerField()
    location = models.CharField(max_length = 255)
    profile_photo = models.ImageField(blank = True)
    pets = models.ManyToManyField(Pet, related_name = &quot;Owners&quot;)

    def get_absolute_url(self):
        return reverse(&quot;owner_profile&quot;, kwargs={&quot;slug&quot;: self.slug})  

    def save(self, *args, **kwargs):  # new
        slug_save(self)
        get_ID(self)
        return super().save(*args, **kwargs)

    def __str__(self):
        return self.name

def slug_save(obj):
    &quot;&quot;&quot; A function to generate a 5 character slug and see if it has been used and contains naughty words.&quot;&quot;&quot;
    if not obj.slug: # if there isn&#39;t a slug
        obj.slug = get_random_string(5) # create one
        slug_is_wrong = True  
        while slug_is_wrong: # keep checking until we have a valid slug
            slug_is_wrong = False
            other_objs_with_slug = type(obj).objects.filter(slug=obj.slug)
            if len(other_objs_with_slug) &gt; 0:
                # if any other objects have current slug
                slug_is_wrong = True
            #if predict(obj.slug):
            #    slug_is_wrong = True
            if slug_is_wrong:
                # create another slug and check it again
                obj.slug = get_random_string(5)

def get_ID(obj):
    obj.id = obj.name + &#39;-&#39; + obj.slug

def get_ID2(obj):
    obj.id = obj.title + &#39;-&#39; + obj.slug

Of relevance are Pet and PetPhoto. Pay attention to those. Notice that Pet stores associated pet_photos in a manytomany field, and afaik you don't need to reverse the data interaction--you can just access it using a reverse set query or something like that.

pet_profile/views.py

from django.shortcuts import render
from django.views.generic import (ListView,
                                  DetailView)
from pet_profile.models import PetOwner, Pet, PetPhoto, PetStory

class PetOwnerListView(ListView):
    model = PetOwner
    context_object_name = &quot;owner_list&quot;
    template_name = &quot;home.html&quot;

class PetOwnerDetailView(DetailView):
    model = PetOwner
    context_object_name = &quot;owner&quot;
    template_name = &quot;pet_owner_profile.html&quot;
    def get_context_data(self, *args, **kwargs):
        context = super().get_context_data(**kwargs)
        context[&#39;pet_photos&#39;] = {}
        pets = PetOwner.objects.filter(name = &quot;pets&quot;)
        for pet in pets:
            context[&#39;pet_photos&#39;][pet] = []
            for photo in pet.pet_photos:
                context[&#39;pet_photos&#39;][pet] += photo
        # context[&#39;pet_photos&#39;] = {pet1: [photo1, photo2, photo3], pet2: [photo1, photo2]}
        return context

class PetListView(ListView):
    model = Pet
    context_object_name = &quot;pet_list&quot;

class PetDetailView(DetailView):
    model = Pet
    context_object_name = &quot;pet&quot;

class PetPhotoListView(ListView):
    model = PetPhoto
    context_object_name = &quot;pet_photo_list&quot;

class PetPhotoDetailView(DetailView):
    model = PetPhoto
    context_object_name = &quot;pet_photo&quot;

class PetStoryListView(ListView):
    model = PetStory
    context_object_name = &quot;pet_story_list&quot;

class PetStoryDetailView(DetailView):
    model = PetStory
    context_object_name = &quot;pet_story&quot;

Most of the action here is in PetOwnerDetailView. By most I mean all of it.

This is where I tried to make a data structure to store pets and their photos because I didn't know how to access the endogenous data structure of the manytomany field. Also you can't iterate over a manytomanyfield in django.

I stored pets as keys in a dict, and associated photos in respective lists as the values. Don't get too hung up on the syntax of my stuff, I know it's probably wrong, and it is, in fact, like the hundredth iteration of how I've tried to do things (store the data for access in the template downstream). So. Apologies.

I learned that you can't iterate over manytomanyfields, even with .all(), so I tried a filter but I am new to filters, and I don't know if "name" refers to the title of the parameter in the model, or to a parameter named "name". I think it's the latter but the internet isn't good at discerning fine-grained questions like this. Why I'm here.

And the template, finally:
pet_owner_profile.html

{% extends &quot;base.html&quot; %}
{% block header %}
{% endblock %}
{% block content %}
    {{ owner.name }}
    {{ owner.age }}
    {{ owner.location }}
    {{ owner.profile_photo }}
    {% for pet in owner.pets.all %}
        {{ pet.name }}
        {{ pet.animaltype }}
        {{ pet.age }}
        &lt;!-- context[&#39;pet_photos&#39;] = {pet1: [photo1, photo2, photo3], pet2: [photo1, photo2]} --&gt;
        {% for photo in pet_photos.pet.photos %}
            &lt;img src = &quot;{{ photo.photo.url }}&quot;&gt;
        {% endfor %}
    {% endfor %}
{% endblock %}

All the weird stuff, like photo.photo.url instead of just photo (wouldn't that be nice), are mostly from suggestions on the internet because I don't ad hoc know how to display an image in django, from a database. Although, photo is the iterable, and .photo is the model parameter. The former is the PetPhoto object, and the latter is the photos parameter. I hope that's clear. .url is something I found on the internet. Yes, I am rationalizing everything, thinking it through, and testing, not blindly, but cautiously.

The second half of this is, whether I am using my static/ directory structure correctly.

So far, I have a static/ folder under the master pet_memorial folder (NOT the project-level pet_memorial sub-folder--nor the pet_profile app folder).

Here are my parameters in settings:

STATIC_URL = &#39;/static/&#39;
STATIC_ROOT = os.path.join(BASE_DIR, &#39;static/&#39;)

I hope that's right but I was hoping someone could correct me if it's wrong.

答案1

得分: 1

假设您在您的 settings.py 文件中正确配置了 MEDIA_ROOTMEDIA_URL 设置以在模板中显示宠物照片。

要在模板中显示宠物照片,您需要将宠物及其关联的照片作为上下文传递到模板中。由于您希望在一个视图中显示来自多个模型的数据,因此创建一个字典会更好,如下所示:

from django.shortcuts import render
from .models import Pet, PetPhoto

def all_pet_photos(request):
    pets = Pet.objects.all()
    pet_data = {}
    for pet in pets:
        pet_data[pet] = PetPhoto.objects.filter(pets=pet)
    context = {'pet_data': pet_data}
    return render(request, 'pet_owner_profile.html', context)

然后在您的模板中,您可以使用 for 循环迭代 pet_data 字典,如下所示:

{% extends 'base.html' %}

{% block content %}
    <h1>Pet Photos</h1>
    {% for pet, photos in pet_data.items %}
        <h2>{{ pet.name }}</h2>
        {% for photo in photos %}
            <img src="{{ photo.photo.url }}" alt="{{ photo.title }}">
        {% endfor %}
    {% endfor %}
{% endblock %}

请注意,这是代码部分的翻译。

英文:

Assuming that MEDIA_ROOT and MEDIA_URL settings are correctly configured in your settings.py file to display the pet photos in the template.

To display the pet photos in a template, you need to pass the pets and their associated photos to the template as context. Since you want to display data from multiple models in one view, so creating a dictionary would be better, like so:

from django.shortcuts import render
from .models import Pet, PetPhoto

def all_pet_photos(request):
    pets = Pet.objects.all()
    pet_data = {}
    for pet in pets:
        pet_data[pet] = PetPhoto.objects.filter(pets=pet)
    context = {&#39;pet_data&#39;: pet_data}
    return render(request, &#39;pet_owner_profile.html&#39;, context)

Then in your template, you can iterate over the pet_data dictionary using a for loop, like so:


{% extends &#39;base.html&#39; %}

{% block content %}
    &lt;h1&gt;Pet Photos&lt;/h1&gt;
    {% for pet, photos in pet_data.items %}
        &lt;h2&gt;{{ pet.name }}&lt;/h2&gt;
        {% for photo in photos %}
            &lt;img src=&quot;{{ photo.photo.url }}&quot; alt=&quot;{{ photo.title }}&quot;&gt;
        {% endfor %}
    {% endfor %}
{% endblock %}

huangapple
  • 本文由 发表于 2023年3月4日 06:07:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/75632266.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定