Django Section 6

yoodeit·2025년 8월 18일
0

일단 만드는 어무해

목록 보기
15/21

0. article 준비작업

핀터레스트처럼 카드형으로 게시물을 띄우기 위해서 자바스크립트 라이브러리인 매직 그리드를 사용한다.
https://jsfiddle.net/eolaojo/4pov0rdf/

article app 만들기

python manage.py startapp articleapp

settings.py에 추가.

urls.py에 추가.

articleapp에 urls.py 생성 및 작성


from django.urls import path
from django.views.generic import TemplateView

app_name = 'articleapp'

urlpatterns = [
    path('list/', TemplateView.as_view(template_name='articleapp/list.html'), name='list'),
]

static에 magicgrid.js 넣기

원래는 magirgrid github에 가면 코드가 있다는데 가니까 비어있길래 관련 벨로그 뒤지다가 코드를 복붙해왔다.

list.html 생성하기

{% extends 'base.html' %}
{% load django_bootstrap5 %}
{% load static %}

{% block content %}

<style>

.container div {
  width: 280px;
  height: 500px;
  background-color: antiquewhite;
  display: flex;
  justify-content: center;
  align-items: center;
  border-radius: 8px;
}

.container .item1 { height: 200px; }
.container .item4 { height: 800px; }
.container .item6 { height: 600px; }
.container .item11 { height: 400px; }

</style>
<!DOCTYPE html>
<div class="container">
  <div class="item1">1</div>
  <div class="item2">2</div>
  <div class="item3">3</div>
  <div class="item4">4</div>
  <div class="item5">5</div>
  <div class="item6">6</div>
  <div class="item7">7</div>
  <div class="item8">8</div>
  <div class="item9">9</div>
  <div class="item10">10</div>
  <div class="item11">11</div>
  <div class="item12">12</div>
  <div class="item13">13</div>
</div>

<script src="{% static 'js/magicgrid.js' %}"></script>

{% endblock %}

매직그리드에서 대부분을 따왔다. static을 사용해야 해서 load static 해준 거랑, script에서 실제로 static 사용한 부분 정도만 눈여겨보면 될 것 같다.

javascript(magirgrid.js) 수정

랜덤으로 사진가져오는 걸로 이미지 띄우기

<div class="container">
  <div class="item1">
    <img src="https://picsum.photos/200/300" alt="">
  </div>

create, detail view 만들기

1) model 만들기

from django.contrib.auth.models import User
from django.db import models

# Create your models here.

class Article(models.Model):
    writer = models.ForeignKey(User, on_delete=models.SET_NULL, related_name='article', null=True)
    # 유저가 지워지면 (알수없음) 이런식으로 뜨도록 하는 세팅이 SET_NULL, related name은 유저객체에서 접근하기 쉬우라고
    title = models.CharField(max_length=200, null=True)
    # 제목은 캐릭터필드. 최대길이 200에 null 허용
    image = models.ImageField(upload_to='article/', null=False)
    content = models.TextField(null=True)
    created_at = models.DateField(auto_created=True, null=True)

2) forms.py 생성 후 작업

from articleapp.models import Article
from django.forms import ModelForm


class ArticleCreationForm(ModelForm):
    class Meta:
        model = Article
        fields=['title','image','content']

똑같이 modelform으로 쉽게쉽게 만들어준다.

3) migrate 해주기

python manage.py makemigrations
python manage.py migrate

4) View 만들기

#from django.shortcuts import render
import profile
from webbrowser import get

from accountapp.decorators import account_authorized
from accountapp.forms import accountUpdateForm
from accountapp.models import Helloworld
from articleapp.forms import ArticleCreationForm
from articleapp.models import Article
from django.contrib.auth.decorators import login_required
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from django.http import (HttpResponse, HttpResponseForbidden,
                         HttpResponseRedirect)
from django.shortcuts import render
from django.urls import reverse, reverse_lazy
from django.utils.decorators import method_decorator
from django.views.decorators import method_decorator
from django.views.generic import CreateView, DeleteView, DetailView, UpdateView
from profileapp.decorators import profile_authorized
from profileapp.forms import ProfileCreationForm
from profileapp.models import Profile


@method_decorator(login_required, 'get')
@method_decorator(login_required, 'post')
class ArticleCreateView(CreateView):
    model = Article
    form_class = ArticleCreationForm
    template_name = 'articleapp/create.html'

    def form_valid(self, form):
        temp_article = form.save(commit=False)
        temp_article.writer = self.request.user
        temp_article.save()
        return super().form_valid(form)

    def get_success_url(self):
        return reverse('articleapp:detail', kwargs={'pk': self.object.pk})
    
class ArticleDetailView(DetailView):
    model = Article
    context_object_name = 'target_article'
    template_name = 'articleapp/detail.html'

5) create.html 만들기


위의 profileapp에서 사용한 create.html과 거의 유사하다. 노란부분만 바뀜.

{% extends 'base.html' %}
{% load django_bootstrap5 %}

{% block content %}

    <div style="text-align: center; max-width: 500px; margin: 4rem auto;">
        <div class="mb-4">
            <h1>Article Create</h1>
        </div>
        <form action="{% url 'articleapp:create' %}" method="post" enctype="multipart/form-data">
            {% csrf_token %}
            {% bootstrap_form form %}
            <input type="submit" class="btn btn-dark rounded-pill col-6 mt-3">
        </form>
    </div>

{% endblock %}

6) 덤으로 detail.html 만들기

{% extends 'base.html' %}
{% block content %}
<div>
    <div style="text-align: center; max-width: 500px; margin: 4rem auto;">
        <h1>
            {{ target_article.title}}
        </h1>
        <img src="{{ target_article.image.url }}" alt="">
        
        <p>
            {{ target_article.content }}
        </p>
        
    </div>
</div>
{% endblock %}

7) urls.py 에 path 추가

from articleapp.views import ArticleCreateView, ArticleDetailView
from django.urls import path
from django.views.generic import TemplateView

app_name = 'articleapp'

urlpatterns = [
    path('list/', TemplateView.as_view(template_name='articleapp/list.html'), name='list'),
    path('create/', ArticleCreateView.as_view(), name='create'),
    path('detail/', ArticleDetailView.as_view(), name='detail'),
] 

수정

update 구현하기

1) UpdateView 만들기 in views.py

@method_decorator(article_authorized, 'get')
@method_decorator(article_authorized, 'post')
class ArticleUpdateView(UpdateView):
    model = Article
    context_object_name = 'target_article'
    form_class = ArticleCreationForm
    template_name = 'articleapp/update.html'

    def get_success_url(self):
        return reverse('articleapp:detail', kwargs={'pk': self.object.pk})

2) decorator 만들기


from articleapp.models import Article
from django.http import HttpResponseForbidden


def article_authorized(func):
    def decorated(request, *args, **kwargs):
        article = Article.objects.get(pk=kwargs['pk'])
        if not article.writer == request.user:
            return HttpResponseForbidden()
        return func(request, *args, **kwargs)
    return decorated

3) detail.html에서 수정버튼 만들기

<a href="{% url 'articleapp:update' pk=target_article.pk %}" >
    <p>update article</p>
</a>

4) url.py path 추가

path('update/<int:pk>', ArticleUpdateView.as_view(), name='update'),

delete 구현하기

1) views.py

@method_decorator(article_authorized,'get')
@method_decorator(article_authorized,'post')
class ArticleDeleteView(DeleteView):
    model = Article
    context_object_name = 'target_article'
    template_name = 'articleapp/delete.html'
    success_url = reverse_lazy('articleapp:list')

2) delete.html

delete.html은 어카운트랑 거의똑같지 뭐...

{% extends 'base.html' %}
{% load django_bootstrap5 %}

{% block content %}

    <div style="text-align: center; max-width: 500px; margin: 4rem auto">
        <div class="mb-4">
            <h4>article delete : {{ target_article.title }} </h4>
        </div>
        <form action="{% url 'articleapp:delete' pk=target_article.pk %}" method="post">
            {% csrf_token %}
            <input type="submit" class="btn btn-danger rounded-pill col-6 mt-3">
        </form>
    </div>

{% endblock %}

3) detail.html

<a href="{% url 'articleapp:delete' pk=target_article.pk %}" >
   <p>delete article</p>
</a>

4) urls.py

path('delete/<int:pk>', ArticleDeleteView.as_view(), name='delete'),

ListView 구현하기

1) views.py

class ArticleListView(ListView):
    model = Article
    content_object_name = 'article_list'
    template_name = 'articleapp/list.html'
    paginate_by = 25

2) urls.py

path('list/', ArticleListView.as_view(), name='list'),

3) list.html

{% extends 'base.html' %}
{% load django_bootstrap5 %}
{% load static %}

{% block content %}
<!DOCTYPE html>
<style>

.container div {
  width: 250px;
  background-color: antiquewhite;
  display: flex;
  justify-content: center;
  align-items: center;

  border-radius: 1rem;
}

.container img {
    width: 100%;
}


</style>

{% if article_list %}
<div class="container">
  {% for article in article_list %}
  <a href="{% url 'articleapp:detail' pk=article.pk %}">
    {% include 'articleapp/snippets/card.html' with article=article %}
  </a>
  {% endfor %}
</div>
<script src="{% static 'js/magicgrid.js' %}"></script>
  {% else %}
<div style="text-align: center;">
  <h1>No Articles yet</h1>
</div>
  
{% endif %}

{% include 'articleapp/snippets/pagination.html' with page_obj=page_obj %}
<div style="text-align: center;">
    <a href="{% url 'articleapp:create' %}" class="btn btn-dark rounded-pill col-3 mt-3 mb-3">
        create article
    </a>
</div>



{% endblock %}

4) snnipets/card.html

<div>
      <img src="{{article.image.url}}" alt="">
</div>

5) snnipets/pagination.html

<div style="text-align: center; margin: 1rem 0;">
    {% if page_obj.has_previous %}
    <a href="{% url 'articleapp:list' %}?page={{ page_obj.previous_page_number }}"
    class="btn btn-secondary rounded-pill">
    {{ page_obj.previous_page_number }}
    </a>
    {% endif %}
    <a href="{% url 'articleapp:list' %}?page={{ page_obj.number }}"
    class = "btn btn-secondary rounded-pill active">
    {{ page_obj.number }} 
    </a>
    {% if page_obj.has_next %}
    <a href="{% url 'articleapp:list' %}?page={{ page_obj.next_page_number }}"
    class="btn btn-secondary rounded-pill">
    {{ page_obj.next_page_number }}
    </a>
    {% endif %}
</div>
profile
Yoodeit

0개의 댓글