I would like to use ListView to generate lists of degrees by school and state. For example, I would want to use to the degree_awarded field in the Program model to make a list of "Schools in Pennsylvania Where You Can Get A Bachelor Degree."
Models
class State(models.Model):
state_name = models.CharField(max_length=20, default='')
state_slug = models.SlugField()
def __str__(self):
return self.state_slug
class City(models.Model):
city_name = models.CharField(max_length=55, default='')
city_slug = models.SlugField()
state = models.ForeignKey(State, null=True)
def __str__(self):
return self.city_slug
class School(models.Model):
school_name = models.CharField(max_length=55, default='')
school_slug = models.SlugField()
city = models.ForeignKey(City, null=True)
def __str__(self):
return self.school_slug
def sorted_programs(self):
return self.program_set.all().order_by('program_name')
class Program(models.Model):
program_name = models.CharField(max_length=100, default='')
program_slug = models.SlugField(max_length=100, default='')
school = models.ForeignKey(School, null=True)
degree_awarded = models.CharField(max_length=10, default='')
def __str__(self):
return self.program_slug
Views
class SchoolIndexView(ListView):
model = State
def get_context_data(self, **kwargs):
context = super(SchoolIndexView, self).get_context_data(**kwargs)
context['state_list'] = State.objects.all().order_by('state_name')
return context
class ProgramInSchoolView(DetailView):
model = School
template = 'school_detail.html'
def get_object(self):
school_slug = School.objects.get(
school_slug=self.kwargs.get('school_slug')
)
return school_slug
def get_context_data(self, **kwargs):
context = super(ProgramInSchoolView, self).get_context_data(**kwargs)
context['school'] = self.object
context['program_list'] = self.object.program_set.all().order_by('program_name')
return context
class ProgramView(DetailView):
model = Program
template = 'program_detail.html'
def get_object(self):
program_slug = Program.objects.get(
program_slug=self.kwargs.get('program_slug')
)
return program_slug
def get_context_data(self, **kwargs):
context = super(ProgramView, self).get_context_data(**kwargs)
context['program'] = self.object
context['program_classes'] = self.object.classes_set.all().order_by('class_number')
return context
class DegreeView(ListView):
model = Program
template = 'degree_list.html'
def get_context_data(self, **kwargs):
context = super(DegreeView, self).get_context_data(**kwargs)
context['state_for_degree'] = State.objects.get(state_slug=self.kwargs.get('state_slug'))
context['city_for_degree'] = City.objects.get(city_name=self.kwargs.get('city_name'))
context['school_for_degree'] = School.objects.get(school_slug=self.kwargs.get('school_slug'))
context['degree_by_state'] = Program.objects.get(degree_awarded=self.kwargs.get('degree_awarded')).filter(school_slug=school_slug).filter(city_slug=city_slug).filter(state_slug=state_slug).order_by('city_name')
return context
Urls
url(r'^(?P<state_slug>[\w-]+)/$', CityInStateView.as_view(), name='state_detail'),
url(r'^(?P<state_slug>[\w-]+)/(?P<school_slug>[\w-]+)/$', ProgramInSchoolView.as_view(), name='school_detail'),
url(r'^(?P<state_slug>[\w-]+)/(?P<school_slug>[\w-]+)/(?P<program_slug>[\w-]+)/$', ProgramView.as_view(), name='program_detail'),
url(r'^(?P<state_slug<[\w-]+)/(?P<program_slug>[\w-]+)/$', DegreeView.as_view(), name='degree_list'),
I cannot figure out how to write the view and url routing so that I can use the information I already have in the degree_awarded field of the Program model to generate a list.
Can someone please point me in the right direction?
Thank you.
Edit: I've added the relevant model(s) that I left out before. I wanted to be concise. I've also added in the last filter that I tried. It is still not producing the degree lists sorted by state.
I don't think you need all that code in the get_context_data method. You should customize the list of objects to display in the get_queryset method.
class DegreeView(ListView):
model = Program
template = 'degree_list.html'
def get_queryset(self):
queryset = super(DegreeView, self).get_queryset()
# batchelors degrees only
queryset = queryset.filter(program_slug=self.kwargs['program_slug'])
# filter by state
queryset = queryset.filter(school__city__state__slug=self.kwargs['state_slug'])
return queryset
See the docs on lookups that span multiple relationships to explain the school__city__state__slug filter.
Then in your template, you can loop through the programs, for example:
{% for program in object_list %}
{{ program.degree_awarded %}
Related
Here is the case, I need the last records of the model to be displayed on the page, for this I added a new pub_date record in the models to add to the queue of records, I also added this to views.py, and it kind of displays, but both records.
views.py code
class IndexView(generic.ListView):
template_name = 'Homepage/index.html'
model = Goods
context_object_name = 'goods'
def description(self):
return self.description_text
def price(self):
return self.price_text
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
numbers = Number.objects.all()
context['numbers'] = numbers
return context
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
avaibilitys = Avaibility.objects.order_by('-pub_date')
context['avaibilitys'] = avaibilitys
return context
models.py code
class Avaibility(models.Model):
name_text = models.CharField(max_length=200)
apply_text = models.CharField(max_length=200)
presence_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published', null=True)
def __str__(self):
return self.name_text
def __str__(self):
return self.apply_text
def __str__(self):
return self.presence_text
this is what displays
You are just sorting the data using order_by and assign the sorted data to a variable:
avaibilitys = Avaibility.objects.order_by('-pub_date')
If you want to get only one of them, you can do this:
avaibilitys = Avaibility.objects.order_by('-pub_date').first()
EDIT
If you want the last one, do this:
avaibilitys = Avaibility.objects.order_by('-pub_date').last()
I have two questions in regards to the problem I am currently facing:
Is it best practice in django to overwrite the post method in the CreateView? If it isn't do you write a form _valid function on the CategoryFullForm or in the CreateView and how would it look? The CreateView currently works great, but want to make sure there isn't a better way to do this.
If this is best practice, how would you override the get function in the UpdateView so you would be able to edit the files that relate to the instance being uploaded and even add an additional File? Open to other ways to accomplish this task as well.
models.py
############ CATEGORY MODEL ################
class Category(models.Model):
category_id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100, null=True, blank=True)
est_pr_sqft = models.FloatField(blank=True)
est_duration = models.IntegerField(blank=True)
preceding_job = models.CharField(max_length=100, blank=True)
category_notes = models.CharField(max_length=250, blank=True)
category_slug = models.SlugField(max_length=100, unique=True, null=False)
author = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
date_posted = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('category-detail', kwargs={'slug': self.category_slug})
def save(self, *args, **kwargs):
if not self.category_slug:
self.category_slug = slugify(self.name)
return super().save(*args, **kwargs)
class CategoryFiles(models.Model):
category_id = models.ForeignKey(Category, on_delete=models.CASCADE)
document = models.FileField(upload_to="attachments",null=True,blank=True)
uploaded_date = models.DateTimeField(auto_now_add=True)
modified_date = models.DateTimeField(auto_now=True)
def delete(self, *args, **kwargs):
self.document.delete()
return super().delete(*args, **kwargs)
forms.py
class CategoryForm(forms.ModelForm):
class Meta:
model = Category
fields = ['name', 'est_pr_sqft', 'est_duration', 'preceding_job', 'category_notes']
class CategoryFullForm(CategoryForm):
files = forms.FileField(widget=forms.ClearableFileInput(attrs={'multiple': True}),required=False)
class Meta(CategoryForm.Meta):
fields = CategoryForm.Meta.fields + ['files']
views.py
####### CATEGORY VIEWS ########
class CategoryListView(LoginRequiredMixin, ListView):
model = Category
template_name = 'ProjectManagementApp/PM-Category-List.html'
context_object_name = 'categories'
slug_field = 'project_slug'
success_url = reverse_lazy('Category-list')
class CategoryDetailView(LoginRequiredMixin, DetailView):
model = Category
template_name = 'ProjectManagementApp/PM-Category-Detail.html'
slug_field = 'category_slug'
def get_context_data(self, **kwargs):
context = super(CategoryDetailView, self).get_context_data(**kwargs)
context['files'] = CategoryFiles.objects.all()
#context['photos'] = CategoryPhotos.objects.all()
return context
class CategoryCreateView(LoginRequiredMixin, CreateView):
model = Category
form_class = CategoryFullForm
template_name = 'ProjectManagementApp/PM-Category-Create.html' # Replace with your template.
#success_url = reverse_lazy('category-detail')
slug_field = 'category_slug'
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
files = request.FILES.getlist('files')
if form.is_valid():
author = request.user
name = form.cleaned_data['name']
est_pr_sqft = form.cleaned_data['est_pr_sqft']
est_duration = form.cleaned_data['est_duration']
preceding_job = form.cleaned_data['preceding_job']
category_notes = form.cleaned_data['category_notes']
category_obj = Category.objects.create(name=name,est_pr_sqft=est_pr_sqft,est_duration=est_duration,preceding_job=preceding_job,category_notes=category_notes,author=author)
for f in files:
CategoryFiles.objects.create(category_id=category_obj,document=f)
return HttpResponseRedirect(reverse_lazy('Category-list'))
else:
return self.form_invalid(form)
class CategoryUpdateView(LoginRequiredMixin, UpdateView): #UserPassesTestMixin - makes sure user who made entry is only one who can update it.
model = Category
form_class = CategoryFullForm
template_name = 'ProjectManagementApp/PM-Category-Create.html' # Replace with your template.
slug_field = 'category_slug'
success_url = reverse_lazy('Category-list')
def post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
files = request.FILES.getlist('files')
if form.is_valid():
author = request.user
name = form.cleaned_data['name']
est_pr_sqft = form.cleaned_data['est_pr_sqft']
est_duration = form.cleaned_data['est_duration']
preceding_job = form.cleaned_data['preceding_job']
category_notes = form.cleaned_data['category_notes']
category_obj = Category.objects.create(name=name,est_pr_sqft=est_pr_sqft,est_duration=est_duration,preceding_job=preceding_job,category_notes=category_notes,author=author)
for f in files:
CategoryFiles.objects.create(category_id=category_obj,document=f)
return HttpResponseRedirect(reverse_lazy('Category-list'))
else:
return self.form_invalid(form)
#
# #Doesnt allow you to update other users post
# #def test_func(self):
# # project = self.get_object()
# # if self.request.user == post.author:
# # return True
# # return False
#
#
def form_valid(self,form):
form.instance.author = self.request.user
return super().form_valid(form)
class CategoryDeleteView(LoginRequiredMixin, DeleteView): #UserPassesTestMixin - makes sure user who made entry is only one who can update it.
model = Category
template_name = 'ProjectManagementApp/PM-Category-Delete.html'
slug_field = 'category_slug'
success_url = reverse_lazy('Category-list')
#Doesnt allow you to delete other users post
#def test_func(self):
# project = self.get_object()
# if self.request.user == post.author:
# return True
# return False
If the urls.py/admin.py are needed am happy to provide, but don't think they are needed, as well as any of the html files.
I am building an FAQ system. The models extend from Topic -> Section -> Article. When creating a new Article the User will select a Topic then a Section then the create Article button.
The url will look something like //mysite.org/Topic_PK/Section_PK/Article_Create
In Django it should look like this:
url(r'^ironfaq/(?P<pk>\d+)/(?P<pk>\d+)/article$', ArticleCreateView.as_view(), name=’article-create’)
What I am looking to do is to associate the Section_PK to the Article when the user submits the Article. I have the Section_PK in the URL I need help to figure out how to use it to do this.
Alternatively with this set up I can have a form rendered with a choice selection from the Section_FK in the Articles Model. If when creating the Article upon rendering the template if I could limit the Section choices by the Topic in the form.py this will also work for my needs
The url will look something like //mysite.org/Topic_PK/article/create
In Django the url should look like this:
url(r'^ironfaq/(?P<pk>\d+)/article/create$', ArticleCreateView.as_view(), name=’article-create’)
Both these methods require the Passing of the Topic or Section PK to the view or form thru the URL. If there is a better way to do this I am open to other suggestions.
In Django I have the following Models
class Topic(Audit):
name = models.CharField(max_length=255)
sort = models.SmallIntegerField()
slug = models.SlugField()
class Meta:
verbose_name_plural = "topics"
def __str__(self):
return self.name
def get_absolute_url(self):
return ('faq-topic-detail',(), {'slug': self.slug})
class Section(Audit):
name = models.CharField(max_length=255)
sort = models.SmallIntegerField()
slug = models.SlugField()
topic = models.ForeignKey(Topic,on_delete=models.CASCADE)
class Meta:
verbose_name_plural = "sections"
def __str__(self):
return self.name
def get_absolute_url(self):
return ('faq-section-detail',(), {'topic__slug': self.topic.slug,
'slug': self.slug})
class Article(Audit):
title = models.CharField(max_length=255)
sort = models.SmallIntegerField()
slug = models.SlugField()
section = models.ForeignKey(Section,on_delete=models.CASCADE)
answer = models.TextField()
vote_up = models.IntegerField()
vote_down = models.IntegerField()
view_count = models.IntegerField(default=0)
class Meta:
verbose_name_plural = "articles"
def __str__(self):
return self.title
def total_votes(self):
return self.vote_up + self.vote_down
def percent_yes(self):
return (float(self.vote_up) / self.total_votes()) * 100
def get_absolute_url(self):
return ('faq-article-detail',(), {'topic__slug': self.section.topic.slug,
'section__slug': self.section.slug, 'slug': self.slug})
Mysite Forms
class CreateArticleForm(forms.ModelForm):
class Meta:
model = Article
widgets = {
'answer': forms.Textarea(attrs={'data-provide': 'markdown', 'data-iconlibrary': 'fa'}),
}
fields = ('title','section','answer')
Mysite Views
class TopicCreateView(CreateView):
model = Topic
fields = ['name']
template_name = "faq/form_create.html"
success_url = "/ironfaq"
def form_valid(self, form):
topic = form.save(commit=False)
activity_user = self.request.user.username
activity_date = datetime.datetime.now()
topic.save()
return super(TopicCreateView,self).form_valid(form)
class SectionCreateView(CreateView):
model = Section
fields = ['name', 'topic']
template_name = "faq/form_create.html"
def form_valid(self, form):
section = form.save(commit=False)
activity_user = self.request.user.username
activity_date = datetime.datetime.now()
section.save()
self.success_url = "/ironfaq/%s/%s" % (section.topic.slug,section.slug)
return super(SectionCreateView,self).form_valid(form)
class ArticleCreateView(CreateView):
model = Article
form_class = CreateArticleForm
template_name = "faq/form_create.html"
def form_valid(self, form):
article = form.save(commit=False)
activity_user = self.request.user.username
activity_date = datetime.datetime.now()
article.save()
self.success_url = "/ironfaq/%s/%s/%s" % (article.section.topic.slug,article.section.slug,article.slug)
return super(ArticleCreateView,self).form_valid(form)
Let's say that you have this url
url(r'^ironfaq/(?P<topic_pk>\d+)/article/create$', ArticleCreateView.as_view(), name=’article-create’)
Where topic_pk will be pk of topic you want to be associated with your Article.
Then you just need to retrieve it in view. And this is done like this
class ArticleCreateView(CreateView):
model = Article
form_class = CreateArticleForm
template_name = "faq/form_create.html"
def form_valid(self, form):
article = form.save(commit=False)
# what are this variables for?
activity_user = self.request.user.username
activity_date = datetime.datetime.now()
# here we are getting 'topic_pk' from self.kwargs
article.topic_id = self.kwargs['topic_pk']
article.save()
self.success_url = "/ironfaq/%s/%s/%s" % (article.section.topic.slug,article.section.slug,article.slug)
return super(ArticleCreateView,self).form_valid(form)
All url params are stored in self.args and self.kwargs. Our topic_pk is named parameter and thats why we can get it by doing self.kwargs['topic_pk']
But be sure to validate existence of Topic with such pk before assigning it to your Article
I'm making a study app that involves flashcards. It is divided into subjects. Each subject (biology, physics) has a set of decks (unitone, unittwo). Each deck has a set of cards (terms and definitions). I want my URLs to look like
localhost:8000/biology/unitone/ but I have trouble putting two URL parameters in one URL.
models.py
class Subject(models.Model):
subject_name = models.CharField(max_length=100)
description = models.TextField()
def __str__(self):
return self.subject_name
def get_absolute_url(self):
return reverse('card:index')
class Deck(models.Model):
deck_name = models.CharField(max_length=100)
subject = models.ForeignKey(Subject, on_delete=models.CASCADE)
def __str__(self):
return self.deck_name
class Card(models.Model):
term = models.CharField(max_length=100)
definition = models.TextField()
deck = models.ForeignKey(Deck, on_delete=models.CASCADE)
def __str__(self):
return self.term
views.py
class IndexView(generic.ListView):
template_name = 'card/index.html'
context_object_name = 'subjects'
def get_queryset(self):
return Subject.objects.all()
class SubjectView(DetailView):
model = Subject
slug_field = "subject"
template_name = 'card/subject.html'
class DeckView(DetailView):
model = Deck
slug_field = "deck"
template_name = 'card/deck.html'
urls.py
# localhost:8000/subjects/1 (biology)
url(r'^subjects/(?P<pk>[0-9]+)/$', views.SubjectView.as_view(), name='subject')
# localhost:8000/subjects/1/1 (biology/unitone)
url(r'^subjects/(?P<pk>[0-9]+)/(?P<pk>[0-9]+)/$', views.DeckView.as_view(), name='deck'),
The second URL in urls.py is what I'm having trouble with. It's not a valid URL.
You can't have multiple parameters with the same name. You have to give each parameter a unique name, e.g.:
url(r'^subjects/(?P<pk>[0-9]+)/(?P<deck>[0-9]+)/$', views.DeckView.as_view(), name='deck'),
In the DeckView you can then access them as self.kwargs['pk'] and self.kwargs['deck'].
there's a way by rewriting the DetailViews
urls.py
url(r'^subjects/(?P<subjects>\w+)/(?P<deck>\w+)/$', views.DeckView.as_view(), name='deck'),
views.py
class DeckView(DetailView):
model = Deck
# slug_field = "deck" # you don't need it
template_name = 'card/deck.html'
def get_object(self, subjects, deck):
subject_obj = Subject.objects.filter(subject_name=subjects).first()
obj = Deck.objects.filter(subject=subject_obj,deck_name=deck).first()
return obj
def get(self, request, subjects, deck):
self.object = self.get_object(subjects, deck)
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
then access localhost:8000/subjects/biology/unitone/
I have a form created with Update View class based view. It displays every value in input type fields from database, except values of many to many field.
What should i insert into view or maybe model so user will see what options they have slected (like in built-in admin module).
This is my view:
class StudentUpdateView(LoginRequiredMixin, UpdateView):
form_class = StudentForm
model = Student
template_name = 'forms/modify.html'
def get_context_data(self, **kwargs):
context = super(StudentUpdateView, self).get_context_data(**kwargs)
context['year'] = Setting.objects.get(id=2)
return context
def form_valid(self, form):
if self.request.is_ajax():
self.object = form.save()
return HttpResponse(json.dumps("success"),
mimetype="application/json")
return super(StudentUpdateView, self).form_valid(form)
def form_invalid(self, form):
if self.request.is_ajax():
return HttpResponseBadRequest(json.dumps(form.errors),
mimetype="application/json")
return super(StudentUpdateView, self).form_invalid(form)
EDIT:
This is my model used, as Model Form
And StudentForm does not exist
class Student (models.Model):
name = models.CharField(u'Imię', max_length=40)
surname = models.CharField(max_length=80)
email = models.EmailField(blank=True)
subjects = models.ManyToManyField(Subjects, blank=True)
programs = models.ManyToManyField(Programs, editable=False)
history = models.CharField(max_length=50, editable=False)
added = models.DateTimeField(auto_now=True)
def __unicode__(self):
return u'%s %s'(self.name, self.surname)
class StudentForm(ModelForm):
class Meta:
model = Student