Issue
I have one model Dataset
and another one called DatasetReview
, where DatasetReview
has a foreign key connection to Dataset
. I would like to display all of the DatasetReview
models that are tied to a specific Dataset
in a separate page.
I can currently view each Dataset
like so: http://127.0.0.1:8000/dataset/3/
and would like to see all of the DatasetReview
models for Dataset
3 like so: http://127.0.0.1:8000/dataset/3/reviews
But I am unsure how to set this up.
I am not sure how to phrase this question well so I had difficulty finding other posts discussing how to do something like this. Here is my code:
urls.py
:
from django.urls import path
from .views import (
DatasetListView,
DatasetDetailView,
DatasetCreateView,
DatasetUpdateView,
DatasetDeleteView,
DatasetReviewsView
)
from . import views
urlpatterns = [
path('', DatasetListView.as_view(), name='argo-home'),
path('dataset/<int:pk>/', DatasetDetailView.as_view(), name='dataset-detail'),
path('dataset/new/', DatasetCreateView.as_view(), name='dataset-create'),
path('dataset/<int:pk>/update', DatasetUpdateView.as_view(), name='dataset-update'),
path('dataset/<int:pk>/delete', DatasetDeleteView.as_view(), name='dataset-delete'),
path('dataset/<int:pk>/reviews', DatasetReviewsView.as_view(), name='dataset-review'),
path('about/', views.about, name='argo-about'),
]
views.py
:
from django.shortcuts import render
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.views.generic import (
ListView,
DetailView,
CreateView,
UpdateView,
DeleteView
)
from .models import Dataset, DatasetReview
def home(request):
context = {
'datasets' : Dataset.objects.all(),
}
return render(request, 'argo/home.html', context)
class DatasetListView(ListView):
model = Dataset
template_name = 'argo/home.html' # <app>/<model>_<viewtype>.html
context_object_name = 'datasets'
ordering = ['-date_posted']
class DatasetDetailView(DetailView):
model = Dataset
class DatasetCreateView(LoginRequiredMixin, CreateView):
model = Dataset
fields = ['title', 'description', 'access']
def form_valid(self, form):
form.instance.author = self.request.user
form.instance.affiliation = self.request.user.affiliation
return super().form_valid(form)
class DatasetUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Dataset
fields = ['title', 'description']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def test_func(self):
Dataset = self.get_object()
if self.request.user == Dataset.author:
return True
return False
class DatasetDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Dataset
success_url = '/'
def test_func(self):
dataset = self.get_object()
if self.request.user == dataset.author:
return True
return False
class DatasetReviewsView(DetailView):
model = DatasetReview
success_url = '/'
def test_func(self):
datasetReview = self.get_object()
if self.request.user == datasetReview.author:
return True
return False
def about(request):
return render(request, 'argo/about.html', {'title': 'About'})
models.py
:
from django.db import models
from django.utils import timezone
from django.core.validators import MaxValueValidator, MinValueValidator
from django.urls import reverse
from users.models import User, Affiliation
# from django.contrib.auth.models import User
class Dataset(models.Model):
title = models.CharField(max_length=100)
description = models.TextField()
date_posted = models.DateTimeField(default=timezone.now)
affiliation = models.ForeignKey(Affiliation, on_delete=models.CASCADE, related_name='posted_datasets')
access = models.ManyToManyField(Affiliation, related_name='available_datasets')
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('dataset-detail', kwargs={'pk' : self.pk})
class DatasetReview(models.Model):
dataset = models.ForeignKey(Dataset, on_delete=models.CASCADE, related_name='reviews_obj')
reviewer = models.ForeignKey(User, on_delete=models.CASCADE, related_name='reviews')
comments = models.TextField()
rating = models.FloatField(
default=3.0,
validators=[MaxValueValidator(5.0), MinValueValidator(1.0)]
)
def __str__(self):
return self.dataset.title + ' review by ' + self.reviewer.username
def get_absolute_url(self):
return reverse('dataset-review', kwargs={'rpk' : self.pk})
admin.py
:
from django.contrib import admin
from .models import Dataset, DatasetReview
admin.site.register(Dataset)
admin.site.register(DatasetReview)
Any tips would be super helpful, very new to django.
Solution
Figured this out.
The key is within class DatasetReviewsView(DetailView)
in views.py
. I first needed to change this inheritance to a ListView
to enable what I was looking for.
Next, I just needed to provide context for what I wanted to show in my html page. This is easily done by overriding the get_context_data
function, which provides the context data for templates displaying this class based view.
It makes it very easy to leverage python to provide the information I want. I could query the id of the dataset I was looking at with id = self.kwargs['pk']
(which must be included in the function parameters for get_context_data
), and then I could just filter all reviews to just those with a dataset matching this id. Within the html I could then iterate over the variable num_reviews
.
There is some other code that averages all the ratings to provide an overall rating as well.
class DatasetReviewsView(ListView):
model = DatasetReview
context_object_name = 'reviews'
success_url = '/'
def get_context_data(self, **kwargs):
id = self.kwargs['pk']
context = super(DatasetReviewsView, self).get_context_data(**kwargs)
context['id'] = id
context['name'] = Dataset.objects.get(pk=id).title
context['num_reviews'] = len(DatasetReview.objects.filter(dataset=id))
tot = 0
for review in DatasetReview.objects.filter(dataset=id):
tot += review.rating
context['avg_rating'] = tot / context['num_reviews']
return context
Answered By - donallw
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.