reference FileField from another model in a view

Hello, This is my first time posting in here and I am relatively new to Django. I’m working on a project that has the following models:

class Skill(models.Model):
    name = models.CharField(max_length=100)
    display = models.BooleanField(default=False)
    icon = models.FileField(upload_to='icons/', blank=True)

    def __str__(self):
        return self.name

class Project(models.Model):
    title = models.CharField(max_length=200)
    slug = models.SlugField(max_length=200)
    description = RichTextField()
    project_url = models.CharField(max_length=200)
    code_url = models.CharField(max_length=200)

    def __str__(self):
        return self.title

class ProjectSkill(models.Model):
    title = models.ForeignKey(Project, on_delete=models.CASCADE)
    name = models.ForeignKey(Skill, related_name='name+', 
                 on_delete=models.CASCADE)
    icon = models.ForeignKey(Skill, related_name='Skill.icon+', 
               on_delete=models.CASCADE)

I’m trying to figure out how to make it so that when I navigate to a Project page on the site, it only passes in the name and icon for the skills associated with that particular project. I can get the name to come through, but the best I’ve gotten on the icon is the name value. Is there a specific way to references the file location from Skill.icon within ProjectSkill? I’m attempting the following in my view:

def project(request, project_slug):
    project = get_object_or_404(Project, slug=project_slug)
    project_skills = ProjectSkill.objects.select_related('name', 'icon').all()

    context = {
        'project': project,
        'skills': project_skills,
    }
    return render(request, 'pages/project.html', context)

Any advice or guidance is most appreciated.

When you’re accessing a collection of items “backwards” through a ForeignKey definition, you’re doing it through what Django calls a “related manager”.

Briefly, the collection of all “ProjectSkill” objects related to an individual “Project” object (named “my_project”, below) would look something like this:

project_skills = my_project.projectskill_set.all()

Since “projectskill_set” is a manager, you have all the manager-related methods available to you.

The query you wrote:

would be returning all instances of ProjectSkill.

You could rewrite your query to filter on project, such that it looked like this:
project_skills = ProjectSkill.objects.filter(title=project)

Thanks so much for the reply @KenWhitesell. I’m assuming I’m not familiar enough with the “related manager” functionality, and what I’ve read in the Django documentation isn’t exactly clear for a beginner like me. I’ve tried to incorporate what you’ve offered up as a solution, but to no avail.

What I am trying to accomplish is this; I created the Skill model so that I could compile a master list of skills to display on a portfolio homepage. I have a Project model that will be used to display projects on their own pages of the website. Each project has a list of skills featured that come from the master skill list. I want to be able to access all of the associated skills’ full range of attributes (name, icon, etc.) to display on the project page without having to create a duplicate database entry.

I’m thinking my model structure and relations aren’t ideal as they are currently set. Short of retrieving all instances from the Skill model and looping through them to filter out anything unrelated to the project, I just can’t quite figure out how to get the skills and their attributes so they are available in my template.

By setting the collection of “Project Skills” in the manner that you described, if done correctly would that allow for accessing all of the attributes of each skill in that collection in my template? For example, would I be able to do the following in my HTML:

{% for skill in skills%}
<img src="{{ skill.icon.url }}">
{% endfor %}

What did you try, and what happened when you tried it?

Yes.

This is going to eventually become confusing, because skills as you’re supplying it in your context is actually a set of ProjectSkill objects, not Skill objects.
This means that within your template, skill is a ProjectSkill, so skill.icon is the ForeignKey field within ProjectSkill. This then means that skill.icon.url is trying to reference a member variable named url within Skill, which doesn’t exist.

What may help you the most here is to use the manage.py shell (or shell_plus from the django-extensions package) to experiment with what is available to you from referencing these different objects. (That, and focus on what the object types are when creating names for things, and what references you’re actually accessing by those names.)

In my view function, I tried changing project_skills to the following:

project_skills = project.projectskills_set.all()

project_skills = Project.objects.get(slug=project_slug).projectskills_set.all()

I even tried just…

project_skills = my_project.projectskills_set.all()

I know I probably didn’t pick the best name for my context, as it could create confusion. And I know that each time, as you’ve stated, I’m only getting back the ProjectSkill objects, when what I need is the Skill objects associated with the given project. I just can’t figure out how to get those Skill objects though.

I haven’t quite figured out the Django shell yet. Every time I’ve tried to use it I get errors. I guess I need to take some time and learn that. Again, still very new at all things Django.

Whenever you’re posting the results of changes in code like this, please post the complete edited view. When you just post individual lines out of context, it’s really difficult to understand what’s going on.

To follow a ForeignKey reference forward, you’re just accessing attributes of a Model.

Let’s say you have an instance of ProjectSkill named a_project_skill.

a_project_skill.icon then is not accessing an icon. It’s a reference to a Skill object.

You really need to change your names here.

I had only changed the project_skills variable in the view function that was at the start of my last response. I tried the three different options that I listed for that variable. I’m sure I will figure it out eventually.

I appreciate you taking time out of your day to try and help me.

If you haven’t yet worked your way through the Django tutorial, this is probably a good time for you to do that. It has examples of doing everything we’ve talked about here so far, including making references through reverse Foreign Keys and using the Django shell.

I spent some time in the Django shell, exploring the objects being returned and found what I was missing.

@KenWhitesell, thanks for pointing me in the right direction.

I wasn’t calling it correctly in my template. Before I was using:

{% for skill in project_skills %}
<figure>
    <img class="skill-icon" src="{{ skill.icon.url }}" >
    <figcaption>{{ skill.name }}</figcaption>
</figure>
{% endfor %}

Once I add your recommendatioin into my view function:

I was able to access what I needed like this:

{% for skill in project_skills %}
<figure>
    <img class="skill-icon" src="{{ skill.name.icon.url }}" >
    <figcaption>{{ skill.name.name }}</figcaption>
</figure>
{% endfor %}