장고:글쓰기: 두 판 사이의 차이

학교의 모든 지식. SMwiki
둘러보기로 이동 검색으로 이동
 
(사용자 3명의 중간 판 31개는 보이지 않습니다)
1번째 줄: 1번째 줄:
{{장고}}
{{장고}}


==개요==
=글 보기=
글쓰기 기능을 구현하기 위해 정리한 문서.
리스트에서 글을 클릭하면 글의 상세내용을 볼 수 있어야 한다.
{| class="wikitable"
!
!일반적으로 코드를 짤 경우
!제네릭뷰(클래스형 뷰)를 쓰는 경우
|-
|view 작성
|글 하나의 내용을 본다.
get_object_or_404를 쓴다. id에 해당하는 객체가 없으면 서버에서 에러를 일으키는데, 이는 서버에러로 잡힌다.


기본적으로 포스팅 기능이지만, 모델명을 Post로 하기엔 전송방식인 post와 혼동이 오곤 해서 question으로 구현한다.
에러의 방향성을 명확히 지정해주기 위해. 404에러를 부르게끔!<syntaxhighlight lang="python">
from django.shortcuts import render, get_object_or_404
from .models import * #모델을 불러온다.


==url 작성==
def detail(request,question_id):#url에서 매핑된 question_id가 전달된다.
urls.py 안에 필요한 기능을 다 담아주어야 한다.
    question=get_object_or_404(Question, pk=question_id)
    context={'question':question}
    return render(request, 'detail.html', context)
</syntaxhighlight>
|<syntaxhighlight lang="python">
from django.views import generic


예컨대, 글을 쓴다면 만들기, 편집, 삭제기능을 만들어주어야 하기에 다음과 같이 기입한다.
class DetailView(generic.DetailView):
    model = Question
#템플릿 명이 명시적으로 지정되지 않은 경우에는 자동으로 모델명_detail.html을 템플릿명으로 사용
</syntaxhighlight>urls.py에서 기입할 때 .as_view를 붙여야 함에 유의하고,


    path('question/create/', question_views.question_create, name='question_create'),
제네릭뷰에 경우, 변수명이 지정되어 있다. urls.py에서 <int:pk> 형태로 기입받는다.
|-
|template작성
|경로에 맞게 해주면 되는데, 위 뷰의 경우엔 /앱이름/template/detail.html 에 만들어준다.
만들어진 html파일의 body에 다음과 같이 넣어준다.


    path('question/modify/<int:question_id>/', question_views.question_modify, name='question_modify'),
상황에 맞게 표를 만들든, 목차를 만들든 html을 짜면 될 터.


    path('question/delete/<int:question_id>/', question_views.question_delete, name='question_delete'),
<nowiki>{{변수.속성}}</nowiki> 형태로 필요한 데이터를 가져온다.<syntaxhighlight lang="html">
<h1>{{ question.subject }}</h1>
<div>
    {{ question.content }}
</div>
</syntaxhighlight>
|제네릭 뷰를 쓸 땐 탬플릿에 모델이 object라는 이름으로 전달된다.


<nowiki>#</nowiki>아직 함수를 짜주진 않았지만, 앞으로 만들 함수에 대해 연결해두자.
<code><nowiki>{{object.subject}}</nowiki></code> 형태의 이름을 써서 변수를 사용할 수 있다.
|}
==글 수정==
{| class="wikitable"
!
!일반적으로 코드를 짤 경우
!제네릭뷰(클래스형 뷰)를 쓰는 경우
|-
|template작성
| colspan="2" |위에서 만든 detail.html을 수정한다. 글 수정을 위한 링크를 추가.<syntaxhighlight lang="html">
{% if request.user == question.author %}<!--해당 작성자 일때만 수정버튼이 보이게끔!-->
<a href="{% url '앱이름:modify' question.id%}" class="btn btn-primary">수정</a>
{% endif %}


==모델 작성==
</syntaxhighlight>제네릭 뷰를 쓰는 경우 넘겨주는 변수에 <code>pk=question.id</code> 형태로 넣어주어야 한다.
글에서 포함해야 할 것들이 있다. 작성자, 작성일자, 내용 등의 요소를 포함하여 모델을 작성한다.
|-
|view 작성
|글 작성과 동일하게 get처리와 post 처리를 한 함수에서 다룬다.<syntaxhighlight lang="python">
from .forms import QuestionForm#위에서 작성한 폼 불러오기
from .models import Question #모델을 불러온다.
from django.contrib import messages#넌필드 오류를 반환하기 위한 것


/앱/models.py 안에 작성한다.<syntaxhighlight lang="python">
def modify(request, question_id):#이름을 update로 해도 괜찮았을 듯하다.
from django.contrib.auth.models import User
    question = get_object_or_404(Question, pk=question_id)
from django.db import models
    if request.user != question.author:
        messages.error(request, '수정권한이 없습니다')
        return redirect('앱이름:detail', question_id=question.id)


class Question(models.Model):
    if request.method == "POST":
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='author_question')
        form = QuestionForm(request.POST, instance=question)#받은 내용을 객체에 담는다.
    subject = models.CharField(max_length=200)
        if form.is_valid():
    content = models.TextField()
            question = form.save(commit=False)
     create_date = models.DateTimeField()
            question.author = request.user
     modify_date = models.DateTimeField(null=True, blank=True)
            question.modify_date = timezone.now() # 수정일시 자동 저장
            question.save()
            return redirect('앱이름:detail', question_id=question.id)
     else:#GET으로 요청된 경우.
        form = QuestionForm(instance=question)#해당 모델의 내용을 가져온다!
     context = {'form': form}
    return render(request, 'create.html', context)
</syntaxhighlight>messages.error 가 있으면 넌필드 오류를 반환한다.
|<syntaxhighlight lang="python">
from django.views import generic
from django.urls import reverse_lazy


     def __str__(self):
class update(generic.UpdateView):
         return self.subject
    model = Question
    feilds = ['필드명', '필드명',...]#작성할 필드명 지정
    success_url=reverse_lazy('앱이름:detail')#작성에 성공한 경우 보낼 탬플릿.
    template_name_suffix='_update'
    #사용하는 탬플릿 명을 '모델명_create.html'로 바꾼다는 의미. 접미사만 바꾼다.
    #기본 탬플릿은 '모델명_form.html'로 나타난다.
   
     def form_valid(self, form): # 폼에 이상이 없으면 실행.
        temp = form.save(commit=False)  # 임시 저장. 폼 외의 다른 내용을 조작하고 싶을 때 사용한다.
        조작
        temp.save()  # 최종 저장
         return super().form_valid(form)
</syntaxhighlight>
</syntaxhighlight>
|-
|template작성
| colspan="2" |글 작성과 동일한 탬플릿을 사용한다. 생성칸에 기존의 데이터만 form에 담아서 보내면 되니까!<syntaxhighlight lang="html">
{% extends '연장할탬플릿.html' %}


===설명===
{% block content %}
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value='나타낼 텍스트'>
    </form>
{% endblock %}
</syntaxhighlight>
|}
 
==글 삭제(+에러메시지 띄우기)==
{| class="wikitable"
{| class="wikitable"
!내용
!
!설명
!일반적으로 코드를 짤 경우
!제네릭뷰(클래스형 뷰)를 쓰는 경우
|-
|-
|author = models.ForeignKey
|template작성
|author속성을 외부의 객체를 불러온다는 의미.
+JQuery
|-
| colspan="2" |위에서 만든 detail.html을 수정한다. 글 수정을 위한 링크를 추가.
|(User, on_delete=models.CASCADE, related_name='author_question')
글 수정과 동일하게, 편집자만 접근할 수 있게끔 if 태그 안에 넣어준다.<syntaxhighlight lang="html">
|
<a href="{% url '앱이름:delete' question.id%}" class="btn btn-primary">삭제</a>
|-
</syntaxhighlight>제네릭 뷰를 쓰는 경우, 넘겨주는 변수에 <code>pk=question.id</code> 형태로 넣어주어야 한다.
|subject = models.CharField(max_length=200)
 
|제목은 200자까지 문자열로 받는다.
 
|-
이번엔 특수한 기능을 위해 JQuery도 함께 작성한다. 에러 메시지를 띄우기 위한 확인창을 구현하기 위해서.
|create_date = models.DateTimeField()
 
|시간을 담는 공간이다.
기존의 작성법과 조금 다르다. 다음과 같이 수정해주자.<syntaxhighlight lang="html">
|-
<a href="#" data-uri="{% url '앱이름:delete' question.id  %}" class="delete">삭제</a>
|modify_date = models.DateTimeField(null=True, blank=True)
<!-- data-uri 속성은 jQuery에서 $(this).data('uri') 형태로 받아가기 위해서 작성-->
|수정시간을 담는다.
|}


===모든 필드에서 쓰는 옵션===
null=True는 빈 값도 인정한다는 의미이다. 기본적으로 False.


===자주 사용하는 필드===
<script type='text/javascript'>
{| class="wikitable"
$(document).ready(function(){
!내용
    $(".delete").on('click', function() { //delete를 포함하는 클래스가 있으면 작동한다.
!설명
        if(confirm("님, 정말로 지움??")) {
!옵션
            location.href = $(this).data('uri');
        }
    });
});
</script>
<!-- 취소를 누르면 아무 일도 하지 않는다.-->
</syntaxhighlight>JQuery가 포함되어있지 않으면 사용할 수 없다.
|-
|-
|models.CharField
|view 작성
|텍스트 필드.
|<syntaxhighlight lang="python">
(글자제한을 위해)
from .models import Question #모델을 불러온다.
|
 
{| class="wikitable"
@login_required(login_url='membership:login')
|+
def delete(request, question_id):
!옵션
    question = get_object_or_404(Question, pk=question_id)
!설명
    if request.user != question.author:
|-
        messages.error(request, '삭제권한이 없습니다')
|max_length=숫자
        return redirect('앱이름:detail', question_id=question.id)
|해당숫자만큼 제한한다.
    question.delete()
|}
    return redirect('앱이름:index')
|-
</syntaxhighlight>
|models.TextField
|<syntaxhighlight lang="python">
|텍스트 필드.
from django.views import generic
(글자제한 없음)
from django.urls import reverse_lazy
|
|-
|models.DateTimeField
|시간담기
|
|}
이외 내용은 장고 공식 메뉴얼의 필드 레퍼런스를 참조. https://docs.djangoproject.com/en/3.1/ref/models/fields/#db-index


==뷰 작성==
class create(generic.DeleteView):
/앱/views/question_view.py 따위의 이름으로 작성한다.
    model = Question
{| class="wikitable"
    success_url=reverse_lazy('앱이름:list')#작성에 성공한 경우 보낼 탬플릿.
|+
    template_name_suffix='_delete'
!
    #사용하는 탬플릿 명을 '모델명_delete.html'로 바꾼다는 의미. 접미사만 바꾼다.
!
    #기본 탬플릿은 '모델명_confirm_delete.html'로 나타난다.
</syntaxhighlight>
|-
|-
|글작성
|template작성
|<syntaxhighlight lang="python">
|글 삭제 확인페이지.(JQuery를 이용한 경우엔 해당사항 없음.)
from django.contrib.auth.decorators import login_required #로그인이 있어야 가능함
글 작성에서 사용한 탬플릿에서 버튼 이름만 바꾼다.
from django.shortcuts import render, get_object_or_404, redirect
from django.utils import timezone


from ..forms import QuestionForm #폼을 불러온다.
(post요청을 통해 들어온 경우에만 삭제하게끔 view 변형 필요.)<syntaxhighlight lang="html">
from ..models import Question #모델을 불러온다.
{% extends '연장할탬플릿.html' %}


{% block content %}
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value='나타낼 텍스트'>
    </form>
{% endblock %}
</syntaxhighlight>
|글 삭제 확인페이지로 이동한다.(JQuery를 이용한 경우엔 해당사항 없음.)<syntaxhighlight lang="html">
{% extends '연장할탬플릿.html' %}


@login_required(login_url='common:login')
{% block content%}
def question_create(request):
<form action="" method='post'>
     if request.method == 'POST':#포스트로 요청이 들어온다면... 글을 올리는 기능.
    {% csrf_token %}
        form = QuestionForm(request.POST) #폼을 불러와 내용입력을 받는다.
     <div class="alert alert-danget">나타낼텍스트 {{나타낼변수}} 자유롭게 사용.</div>
        if form.is_valid():
    <input type="submit" value="Delete" class="btn btn-danget"
            question = form.save(commit=False)
</form>
            question.author = request.user  # 추가한 속성 author 적용
{% endblock %}
            question.create_date = timezone.now()
            question.save()
            return redirect('pybo:index') #작성이 끝나면 목록화면으로 보낸다.
    else:#포스트 요청이 아니라면.. form으로 넘겨 내용을 작성하게 한다.
        form = QuestionForm()
    context = {'form': form}
    return render(request, 'pybo/question_form.html', context)
</syntaxhighlight>
</syntaxhighlight>
|-
|글수정
|
|-
|
|from django.contrib import messages
|}
|}
<br />
==tip==
뷰가 많아지면 /앱/views/posting_view.py 따위의 이름으로 뷰를 나누어 작성하는 것도 전략이다.

2021년 6월 25일 (금) 14:26 기준 최신판

장고! 웹 프레임워크! 틀:장고

  1. 장고:개요
  2. 장고:웹페이지설계
    1. 장고:앱
    2. 장고:url
    3. 장고:model
      1. 장고:DB
      2. 장고:모델 필드
      3. 장고:모델의 변경
    4. 장고:view
      1. 장고:클래스형 뷰, 제네릭 뷰
      2. 장고:view 각종 기능
    5. 장고:template
    6. 장고:static. 정적파일 사용하기
      1. 장고:CSS 사용하기
      2. 장고:JS 사용하기
      3. 장고:글꼴 사용
      4. 장고:부트스트랩
    7. 장고:media. 미디어 파일 사용하기
  3. 장고:관리자페이지
  4. 장고:settings.py
  5. 장고:기능구현
    1. 장고:회원관리
    2. 장고:유저
    3. 장고:커스텀 유저
    4. 장고:소셜로그인
    5. 장고:입력받기
    6. 장고:저장된 내용 활용하기
    7. 장고:변수 내보내기
    8. 장고:글쓰기
    9. 장고:페이징(페이지나누기)
    10. 장고:답변쓰기(댓글쓰기)
    11. 장고:추천,즐겨찾기
    12. 장고:새글(최신글) 나타내기
    13. 장고:썸머노트 설치
    14. 장고:네비게이션 바 만들기
    15. 장고:검색기능
    16. 장고:카테고리 만들기
    17. 장고:사진 올리기
    18. 장고:파일 업로드
    19. 장고:이메일app 만들기
    20. 장고:매직 그리드
  6. 장고:웹서비스
    1. 장고:Git
    2. 장고:리눅스에 올리기
    3. 장고:우분투에 올리기(nginx 사용)
    4. 장고:도커로 올리기
  7. 장고:팁

글 보기[편집 | 원본 편집]

리스트에서 글을 클릭하면 글의 상세내용을 볼 수 있어야 한다.

일반적으로 코드를 짤 경우 제네릭뷰(클래스형 뷰)를 쓰는 경우
view 작성 글 하나의 내용을 본다.

get_object_or_404를 쓴다. id에 해당하는 객체가 없으면 서버에서 에러를 일으키는데, 이는 서버에러로 잡힌다.

에러의 방향성을 명확히 지정해주기 위해. 404에러를 부르게끔!
from django.shortcuts import render, get_object_or_404
from .models import * #모델을 불러온다.

def detail(request,question_id):#url에서 매핑된 question_id가 전달된다.
    question=get_object_or_404(Question, pk=question_id)
    context={'question':question}
    return render(request, 'detail.html', context)
from django.views import generic

class DetailView(generic.DetailView):
    model = Question
#템플릿 명이 명시적으로 지정되지 않은 경우에는 자동으로 모델명_detail.html을 템플릿명으로 사용
urls.py에서 기입할 때 .as_view를 붙여야 함에 유의하고,

제네릭뷰에 경우, 변수명이 지정되어 있다. urls.py에서 <int:pk> 형태로 기입받는다.

template작성 경로에 맞게 해주면 되는데, 위 뷰의 경우엔 /앱이름/template/detail.html 에 만들어준다.

만들어진 html파일의 body에 다음과 같이 넣어준다.

상황에 맞게 표를 만들든, 목차를 만들든 html을 짜면 될 터.

{{변수.속성}} 형태로 필요한 데이터를 가져온다.
<h1>{{ question.subject }}</h1>
<div>
    {{ question.content }}
</div>
제네릭 뷰를 쓸 땐 탬플릿에 모델이 object라는 이름으로 전달된다.

{{object.subject}} 형태의 이름을 써서 변수를 사용할 수 있다.

글 수정[편집 | 원본 편집]

일반적으로 코드를 짤 경우 제네릭뷰(클래스형 뷰)를 쓰는 경우
template작성 위에서 만든 detail.html을 수정한다. 글 수정을 위한 링크를 추가.
{% if request.user == question.author %}<!--해당 작성자 일때만 수정버튼이 보이게끔!-->
<a href="{% url '앱이름:modify' question.id%}" class="btn btn-primary">수정</a>
{% endif %}
제네릭 뷰를 쓰는 경우 넘겨주는 변수에 pk=question.id 형태로 넣어주어야 한다.
view 작성 글 작성과 동일하게 get처리와 post 처리를 한 함수에서 다룬다.
from .forms import QuestionForm#위에서 작성한 폼 불러오기
from .models import Question #모델을 불러온다.
from django.contrib import messages#넌필드 오류를 반환하기 위한 것

def modify(request, question_id):#이름을 update로 해도 괜찮았을 듯하다.
    question = get_object_or_404(Question, pk=question_id)
    if request.user != question.author:
        messages.error(request, '수정권한이 없습니다')
        return redirect('앱이름:detail', question_id=question.id)

    if request.method == "POST":
        form = QuestionForm(request.POST, instance=question)#받은 내용을 객체에 담는다.
        if form.is_valid():
            question = form.save(commit=False)
            question.author = request.user
            question.modify_date = timezone.now()  # 수정일시 자동 저장
            question.save()
            return redirect('앱이름:detail', question_id=question.id)
    else:#GET으로 요청된 경우.
        form = QuestionForm(instance=question)#해당 모델의 내용을 가져온다!
    context = {'form': form}
    return render(request, 'create.html', context)
messages.error 가 있으면 넌필드 오류를 반환한다.
from django.views import generic
from django.urls import reverse_lazy

class update(generic.UpdateView):
    model = Question
    feilds = ['필드명', '필드명',...]#작성할 필드명 지정
    success_url=reverse_lazy('앱이름:detail')#작성에 성공한 경우 보낼 탬플릿.
    template_name_suffix='_update'
    #사용하는 탬플릿 명을 '모델명_create.html'로 바꾼다는 의미. 접미사만 바꾼다.
    #기본 탬플릿은 '모델명_form.html'로 나타난다.
    
    def form_valid(self, form):  # 폼에 이상이 없으면 실행.
        temp = form.save(commit=False)  # 임시 저장. 폼 외의 다른 내용을 조작하고 싶을 때 사용한다.
        조작
        temp.save()  # 최종 저장
        return super().form_valid(form)
template작성 글 작성과 동일한 탬플릿을 사용한다. 생성칸에 기존의 데이터만 form에 담아서 보내면 되니까!
{% extends '연장할탬플릿.html' %}

{% block content %}
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value='나타낼 텍스트'>
    </form>
{% endblock %}

글 삭제(+에러메시지 띄우기)[편집 | 원본 편집]

일반적으로 코드를 짤 경우 제네릭뷰(클래스형 뷰)를 쓰는 경우
template작성

+JQuery

위에서 만든 detail.html을 수정한다. 글 수정을 위한 링크를 추가. 글 수정과 동일하게, 편집자만 접근할 수 있게끔 if 태그 안에 넣어준다.
<a href="{% url '앱이름:delete' question.id%}" class="btn btn-primary">삭제</a>
제네릭 뷰를 쓰는 경우, 넘겨주는 변수에 pk=question.id 형태로 넣어주어야 한다.


이번엔 특수한 기능을 위해 JQuery도 함께 작성한다. 에러 메시지를 띄우기 위한 확인창을 구현하기 위해서.

기존의 작성법과 조금 다르다. 다음과 같이 수정해주자.
<a href="#" data-uri="{% url '앱이름:delete' question.id  %}" class="delete">삭제</a>
<!-- data-uri 속성은 jQuery에서 $(this).data('uri') 형태로 받아가기 위해서 작성-->


<script type='text/javascript'>
$(document).ready(function(){
    $(".delete").on('click', function() { //delete를 포함하는 클래스가 있으면 작동한다.
        if(confirm("님, 정말로 지움??")) {
            location.href = $(this).data('uri');
        }
    });
});
</script>
<!-- 취소를 누르면 아무 일도 하지 않는다.-->
JQuery가 포함되어있지 않으면 사용할 수 없다.
view 작성
from .models import Question #모델을 불러온다.

@login_required(login_url='membership:login')
def delete(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    if request.user != question.author:
        messages.error(request, '삭제권한이 없습니다')
        return redirect('앱이름:detail', question_id=question.id)
    question.delete()
    return redirect('앱이름:index')
from django.views import generic
from django.urls import reverse_lazy

class create(generic.DeleteView):
    model = Question
    success_url=reverse_lazy('앱이름:list')#작성에 성공한 경우 보낼 탬플릿.
    template_name_suffix='_delete'
    #사용하는 탬플릿 명을 '모델명_delete.html'로 바꾼다는 의미. 접미사만 바꾼다.
    #기본 탬플릿은 '모델명_confirm_delete.html'로 나타난다.
template작성 글 삭제 확인페이지.(JQuery를 이용한 경우엔 해당사항 없음.)

글 작성에서 사용한 탬플릿에서 버튼 이름만 바꾼다.

(post요청을 통해 들어온 경우에만 삭제하게끔 view 변형 필요.)
{% extends '연장할탬플릿.html' %}

{% block content %}
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value='나타낼 텍스트'>
    </form>
{% endblock %}
글 삭제 확인페이지로 이동한다.(JQuery를 이용한 경우엔 해당사항 없음.)
{% extends '연장할탬플릿.html' %}

{% block content%}
<form action="" method='post'>
    {% csrf_token %}
    <div class="alert alert-danget">나타낼텍스트 {{나타낼변수}} 자유롭게 사용.</div>
    <input type="submit" value="Delete" class="btn btn-danget"
</form>
{% endblock %}

tip[편집 | 원본 편집]

뷰가 많아지면 /앱/views/posting_view.py 따위의 이름으로 뷰를 나누어 작성하는 것도 전략이다.