웹 프로그래밍

[pinterest clone (27)] articleapp 구현

728x90
반응형

1

models.py

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='arrticle', null=True)
    title = models.CharField(max_length=200, null=True)
    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 django.forms import ModelForm

from articleapp.models import Article


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

3

python manage.py makemigrations

python manage.py migrate

 

4

views.py

from django.contrib.auth.decorators import login_required
from django.shortcuts import render

# Create your views here.
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.views.generic import CreateView, DetailView

from articleapp.forms import ArticleCreationForm
from articleapp.models import Article

@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, detail.html

 

create.html

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

{% block content %}

    <div style="text-align: center; max-width: 500px; margin: 4rem auto">
        <div class="mb-4">
            <h4>Article Create</h4>
        </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 %}

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 %}

 

6

urls.py

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

from articleapp.views import ArticleCreateView, ArticleDetailView

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/<int:pk>', ArticleDetailView.as_view(), name='detail'),
]

7

views.py_ updateview

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

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

 

articleapp/decorators.py 생성

from django.http import HttpResponseForbidden

from articleapp.models import Article


def article_ownership_required(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

 

views.py 데코레이터 적용

@method_decorator(article_ownership_required, 'get')
@method_decorator(article_ownership_required, '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})

 

8

update.html 생성

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

{% block content %}

    <div style="text-align: center; max-width: 500px; margin: 4rem auto">
        <div class="mb-4">
            <h4>Article Update</h4>
        </div>
        <form action="{% url 'articleapp:update' pk=target_article.pk %}" 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 %}

9

views.py_ deleteview

class ArticleDeleteView(DeleteView):
    model = Article
    context_object_name = 'target_article'
    success_url = reverse_lazy('articleapp:list')
    template_name = 'articleapp/delete.html'

 

delete.html 생성

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

{% block content %}

    <div style="text-align: center; max-width: 500px; margin: 4rem auto">
        <div class="mb-4">
            <h4>Delete article : {{ 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 %}

 

urls.py 설정

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

from articleapp.views import ArticleCreateView, ArticleDetailView, ArticleUpdateView, ArticleDeleteView

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/<int:pk>', ArticleDetailView.as_view(), name='detail'),
    path('update/<int:pk>', ArticleUpdateView.as_view(), name='update'),
    path('delete/<int:pk>', ArticleDeleteView.as_view(), name='delete'),


]

 

728x90
반응형