Django: in views how can a redirect if the paramater(linked to a url pattern) does not match a model result?

Question:

I apologize am still learning django and am just hitting my head on a wall with some of it.
The current issue is that I have a view linked through a URL parameter the issue and it works for showing my model info but if you hard type the url to a different parameter it shows the page. I have tried to redirect it but that is not working I feel like it might because of how and where I return my render. Any advice on how to set up url’s base off model and if that model info doesn’t exist either through a 404 or redirect to a different page?

View:

@login_required(login_url='login_register')
def project_page(request, name):
  project = Project.objects.all()
  issueticket1 = Issue.objects.filter(related_project__name__exact=name)
  table = IssueTable(issueticket1)
  project_list = {}
  for p in project:
      if p.name == name:
          project_list = {'id': p.project_id, 'startdate': p.start_date,
                          'enddate': p.end_date, 'description': p.description}
return render(request, 'main_projects.html', {'name': name, 'project_list': project_list, 'project': project, 'table': table})

Urls:

path('projects/<str:name>/', views.project_page, name="project_page"),
Asked By: L30N3CH0

||

Answers:

Don’t know what your exact goal is, it’s not very clear to me so I’m guessing
here.
But the best thing to do when an object doesn’t exist when getting an object is to return a 404 not found error message.

So in your code you’re basically getting all projects and using if statements to check if they match the name.
Which can be quite expensive for the database and aside that looping through all the projects is not advisable, imagine you had 1000 projects in your database it means you’re going through each one of them and checking if the name matches that will prevent the page from loading quickly.

What you can do instead is to use the filter method, and retrieve all projects matching the name.

from django.shortcuts import get_object_or_404, redirect
from django.core.exceptions import ValidationError


@login_required(login_url='login_register')
def project_page(request, name):
    project_list = Project.objects.all()
    issueticket1 = Issue.objects.filter(related_project__name__exact=name)
    table = IssueTable(issueticket1)

    # Instead of looping get the projects get the matching item, you can use get_object_or_404 from django
    project = get_object_or_404(Project, name=name)
    # or an alternative 
    try:
        project = project_list.get(name=name)
    except Project.DoesNotExist:
        raise ValidationError("project does not exist")
        # If you really need to redirect you can
        return redirect("/your-url/")

    return render(request, 'main_projects.html', {'name': name, 'project_list': project_list, 'project': project, 'table': table})
Answered By: kwamito

You could just add an if statement inside your template

{% if issueticket1 %}
  ~Do the Normal things~
{% else %}
  No Match Was Found
{% end if %}

you could also do the 404 thing like @kwamito said, that’s always good.

Or you could just add and if in the view and a custom redirect like:

  • I’m not sure 100% on how this is supposed to work, so just gonna throw two potential redirects in there
@login_required(login_url='login_register')
def project_page(request, name):

  projectMatches = Project.objects.filter(name=name)
  if not projectMatches.count():
    # No Projects
    redirect('errorpage')

  # Note: .first() - it will be the Object or None
  issueticket1 = Issue.objects.filter(related_project__name__exact=name).first()
  if not issueticket1:
    # No Tickets
    redirect('errorpage')

  table = IssueTable(issueticket1)
  project_list = {}

  for p in projectMatches:
    project_list = {'id': p.project_id, 'startdate': p.start_date,
                    'enddate': p.end_date, 'description': p.description}
  return render(request, 'main_projects.html', {'name': name, 'project_list': project_list, 'project': project, 'table': table})

Eitherway you should probably use Project.objects.filter() instead of looping through all of them and manually checking names (make the db do that)


Edit

So it depends on what you are using assigned_users for..

If you are showing the Assigned users with the project, it’s best to just loop through them in the template

view.py
  • Instead of Manually creating + Packing the project_list, just pass the whole QuerySet to the template (it’s a lot less work)
@login_required(login_url='login_register')
def project_page(request, name):

  # removed for space

  #  for p in projectMatches:
  #    project_list = {'id': p.project_id, 'startdate': p.start_date,
  #                    'enddate': p.end_date, 'description': p.description}

  data = {
    'name': name,
    'project_list': projectMatches,
    'project': project,
    'table': table
    }
  return render(request, 'main_projects.html', data)

# If you are dead set on using a dictonary for project_list, try out:
project_list = projectMatches.values()
# project_list = [
#    {'project_id': '{project_id0}', 'start_date': '{start_date0}', 'end_date': '{end_date0}', 'description': '{description0}'},
#    {'project_id': '{project_id1}', 'start_date': '{start_date1}', 'end_date': '{end_date1}', 'description': '{description1}'},
# ]
main_projects.html
<!-- HTML / Template -->
{% for i in project_list %}
  <!-- i is the entire Project Object! -->

  Project ID: {{i.project_id}}<br>
  Start Date: {{i.start_date}}<br>
  End Date: {{i.end_date}}<br>
  Desc: {{i.description}}<br>
  <hr>

  Assigned Users:<br>
  {% for u in i.assigned_users_list.all %}
    {{u}}<br>
  {% endfor %}
  <hr>
{% endfor %}

But lets say you wanted to filter out Assigned User that have ‘completed’ their job for the project, just create a model method and call it in the template

  • This assumes you ditched the manual dictionary packing
models.py
class Project(models.Model):
  project_id = models.CharField(max_length=50, blank=True, null=True)
  description = models.CharField(max_length=200, blank=True, null=True)

  assigned_users_list = models.ManyToManyField('assigned_users', blank=True)

  def users_with_open_tasks(self):
    return self.assigned_users_list.filter(completed=False)

  def usersnames_with_open_tasks(self):
    return self.assigned_users_list.filter(completed=False).values_list('user__username', flat=True)
main_projects.html
<!-- HTML / Template -->
{% for i in project_list %}
  <!-- i is the entire Project Object! -->

  Project ID: {{i.project_id}}<br>
  Start Date: {{i.start_date}}<br>
  End Date: {{i.end_date}}<br>
  Desc: {{i.description}}<br>
  <hr>

  Assigned Users:<br>
  {% for u in i.assigned_users_list.all %}
    {{u}}<br>
  {% endfor %}
  <hr>

  Users with Open Tasks:<br>
  {% for u in i.users_with_open_tasks %}
    {{u}}<br>
  {% endfor %}
  <hr>
{% endfor %}
Answered By: Nealium
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.