Projectapp은 Article을 카테고리별로 묶어주는 기능이다.
class Project(models.Model):
image = models.ImageField(upload_to='project/', null=False)
title = models.CharField(max_length=200, null=True)
description = models.TextField(null=True)
created_at = models.DateField(auto_now_add=True, null=True)
class ProjectCreationForm(ModelForm):
class Meta:
model = Project
fields = ['image', 'title', 'description']
@method_decorator(login_required, 'get')
@method_decorator(login_required, 'post')
class ProjectCreateView(CreateView):
model = Project
form_class = ProjectCreationForm
template_name = 'projectapp/create.html'
def get_success_url(self):
return reverse('projectapp:detail', kwargs={'pk': self.object.pk})
class ProjectDetailView(DetailView):
model = Project
context_object_name = 'target_project'
template_name = 'projectapp/detail.html'
class ProjectListView(ListView):
model = Project
context_object_name = 'project_list'
template_name = 'projectapp/list.html'
paginate_by = 25
profileapp에서 사용했던 레이아웃 그대로 사용 (둥근 image, title, description)
{% extends 'base.html' %}
{% block content %}
<div style="text-align: center; max-width: 700px; margin: 4rem auto;">
<div>
<img src="{{ target_project.image.url }}" alt=""
style="heigth: 12rem; width: 12rem; border-radius: 20rem; margin-bottom: 2rem; object-fit: cover;">
<h2 style="font-family: 'NanumGimYuICe'">
{{ target_project.title }}
</h2>
<h5 style="margin-bottom: 3rem">
{{ target_project.description }}
</h5>
</div>
</div>
{% endblock %}
먼저 list.html
{% extends 'base.html' %}
{% load static %}
{% block content %}
<style>
.container {
padding: 0;
margin: 0, auto;
}
.container div {
display: flex;
justify-content: center;
align-items: center;
border-radius: 1rem;
}
.container img {
width: 7rem;
height: 7rem;
object-fit: cover;
border-radius: 1rem;
}
</style>
{% if project_list %}
<div class="container">
{% for project in project_list %}
<a href="{% url 'projectapp:detail' pk=project.pk%}">
<!-- card의 레이아웃을 바꿀 때 card.html만 수정하면 가능하도록 따로 지정해준다.-->
<!-- card.html에서 필요한 article 객체가 for문에서 정해준 article과 같다는 의미의 코드 작성-->
{% include 'snippets/card_project.html' with project=project %}
</a>
{% endfor %}
</div>
<script src="{% static 'js/magicgrid.js' %}"></script>
{% else %}
<div style="text-align: center">
<h1>No Projects YET!</h1>
</div>
{% endif %}
{% include 'snippets/pagination.html' with page_obj=page_obj %}
<div style="text-align: center">
<a href="{% url 'projectapp:create' %}" class="btn btn-dark rounded-pill mt-3 mb-3 px-3">
Create Project
</a>
</div>
{% endblock %}
card_project.html
<div style="display: block; text-align: center;">
<img src="{{ project.image.url }}" alt="">
<h5 class="mt-2">
<!-- truncatechars로 긴 제목 짜르기-->
{{ project.title | truncatechars:8 }}
</h5>
</div>
pagination.html
?page={{ page_obj.previous_page_number }} 앞에 있던 article에 한정시키는 코드 지우면 모든 app에 적용가능
<a href="?page={{ page_obj.previous_page_number }}"
class="btn btn-secondary rounded-pill">
{{ page_obj.previous_page_number }}
</a>
a {
color: black;
text-decoration: none;
}
a:hover {
color: black;
text-decoration: none;
}
.leebook_header_nav {
margin: 0 0.5rem;
}
.leebook_header_navbar {
margin: 1rem 0;
}
articleapp/models.py에서 projcet를 Foreinkey로 만들어준 후 forms.py에도 적용시켜 create할 때 만들어져 있는 project중 골라서 선택할 수 있도록 한다.
articleapp/models.py
project = models.ForeignKey(Project, on_delete=models.SET_NULL, related_name='project', null=True)
articleapp/forms.py
fields = ['title', 'image', 'project', 'content']
모델을 수정했으니 다시 DB에 연동시키기 위해 makemigration, migrate 작업을 해준다.
이제 Project를 들어갔을 때 그 project인 article들을 보여줘야한다.
articleapp에 commentapp을 Mixin으로 박아 넣었던 것처럼 비슷하게 해야하는데 이 방법은 무조건 다 가져올 때의 방식이고 조건을 달아서 좀 더 여러가지 작업을 하고 싶을 때에는 이런 방식은 한계가 있을 수 있다.
그래서 View안에서 MultipleObjectMixin 이용해 비슷한 방식으로 구현한다.
MultipleObjectMixin은 이름 그대로 여러가지 object를 다룰 수 있게 해준다.
get_context_data 메소드를 이용해서 실질적으로 어떤 게시글을 가져올 것인지에 대한 필터링 구문을 완성한다. object_list 안에 filter를 걸어서 project가 현재 프로젝트의 object와 같은 것을 필터링한다. 그 후 return으로 필터링한 값을 반환해준다.
projectapp/views.py
class ProjectDetailView(DetailView, MultipleObjectMixin):
model = Project
context_object_name = 'target_project'
template_name = 'projectapp/detail.html'
paginate_by = 25
def get_context_data(self, **kwargs):
object_list = Article.objects.filter(project=self.get_object())
return super(ProjectDetailView, self).get_context_data(object_list=object_list, **kwargs)
articleapp/list.html을 복사해서 아무곳에도 종속되지 않은 snippets/list_fragment.html에 list_fragment.html라는 이름으로 붙여넣고 이를 projectapp/detail.html에서 include 구문으로 사용할 것이다.
list_fragment.html는 base.html이랑 관련 없으므로 extends 구문과 block 구문을 지워준다.
article_list를 object_list로 detail.html에서 지정해주면 include 구문으로 그대로 적용하여 사용할 수 있다.
projectapp/detail.html
<div>
{% include 'snippets/list_fragment.html' with article_list=object_list %}
</div>
accountapp/views.py
class AccountDetailView(DetailView, MultipleObjectMixin):
model = User
# 다른 pk로 접속해도 target_user의 정보를 볼 수 있도록 지정해준다.
context_object_name = 'target_user'
template_name = 'accountapp/detail.html'
paginate_by = 25
def get_context_data(self, **kwargs):
object_list = Article.objects.filter(writer=self.get_object())
return super(AccountDetailView, self).get_context_data(object_list=object_list, **kwargs)
accountapp/detail.html
<div>
{% include 'snippets/list_fragment.html' with article_list=object_list %}
</div>