1:N 관계를 ORM에서 어떻게 표현하며 일반 DB와 어떻게 연결되는지 살펴보자.
새로운 프로젝트 생성
- 프로젝트 생성
 
python manage.py startapp blog                #플젝생성
- settings.py 에 blog 라는 앱을 설치 (평소엔 꼭 설치할 필요는 없지만, DB와 연동하려면 설치 필요)
 
INSTALLED_APPS = [..., 'blog',]
- blog/models.py - 테이블 생성
 
from django.db import models
from django.utils import timezone
class Post(models.Model):
    author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
    title = models.CharField(max_length=200)
    text = models.TextField()  # 글자수에 제한 없는 텍스트
    created_date = models.DateTimeField(
        default=timezone.now)  # 날짜와 시간
    published_date = models.DateTimeField(
        blank=True, null=True) #  필드가 폼에서 빈 채로 저장되는 것을 허용
    def publish(self):
        self.published_date = timezone.now()        #수정된 날짜
        self.save()
    def __str__(self):      #print ftn에 적용할 str ftn
        return self.title
auth.User : system table. 시스템이 기본으로 생성한 table
on_delete=models.CASCADE : cascade:종속성. user 지울 때 관련된 post 자동으로 지워라.
blank=True : form에서 빈 데이터 허용 (application level)
null=True : 필드값 optional (database level)
blank=False, null=True : application에서는 값 필수, db차원에서는 값 optional
실제로 blog_post에 생성된 테이블을 보면, id, title, text, created_date, published_date, author_id
기본적으로 class 만들면 기본적으로 id field는 자동으로 만들어진다. (django 규칙)
보통 id값은 auto-increment type 으로 생성된다.
author로 테이블을 만들었는데 author_id로 만들어졌다. 기본적으로 author에도 id field가 있는 것이다.
즉 외래키로 잡힌 것은 underbar 붙여 id 생성된다.
- blog/admin.py - models에서 만든 table 등록
 
from blog.models import Post    #루트폴더/blog/models.py 안의 Post Class를 참조
admin.site.register(Post)
- migration
 
python manage.py makemigrations        #변경사항 기록
python manage.py migrate            #실제 데이터베이스 변경사항 반영
Applying ... OK 떠야 반영 성공된 것이다.
localhost:8000/admin 에서 확인해보자.
auth.User : 시스템 테이블로, admin 화면에서 AUTHENTICATION AND AUTHORIZATION > Users 테이블을 가리킨다.
parameter 전달하는 방법에 차이가 있는 것일 뿐
GET method : 전통적인 방식
정적 URL
동적 URL pattern : 프로그램 전달 방식???
DB Browser에서 blog_post 테이블의 스키마를 보면 REFERENCES "auth_user" ("id") 로 되어있고,
시스템 테이블인 auth_user 테이블 밑의 id는 integer이다.
DB Browser에서는 신경 안 쓰고 작업해도 되지만, 코딩 시에는 auth_user의 'id'를 참고한다는 것을 고려해야 한다.
in Jupyter Notebook for unit test
Post Record 생성
from blog.models import Post
p = Post(title="오늘 점심 메뉴", text="뭐지?")
p.save()
#result
#IntegrityError: NOT NULL constraint failed: blog_post.author_id
로 하면, NOT NULL field에 값을 채워주지 않았기 때문에 오류난다.
from blog.models import Post    #객체 참조
from django.contrib.auth.models import User
u = User.objects.all().get(username='lee')  #system에서 만든 User table
#객체생성방법: 하나는 객체 만들어서 save, 두번째는 그냥 함수 사용
p = Post(title="오늘 점심 메뉴", text="뭐지?", author=u)
p.save()
p.title = "오늘 저엄심 메뉴"
p.save()    #p는 아직 기존 레코드를 가리키고 있으므로 update된다.
in APP
동적 URL 경로 구성
- mysite/urls.py
 
urlpatterns = [
    path('blog/', include('blog.urls')),
    ...
]
blog폴더 내의 urls.py를 불러오고, 기본 경로는 blog/ 로 시작한다.
- blog/urls.py
 
urlpatterns = [
    path('<name>/', views.index2),      #<name> parameter에 대해 동적으로 mapping
    path('<int:pk>/detail', views.index3),
]
으로 사용하여, 동적으로 URL을 mappping할 수 있다.
pk라는 파라미터를 앞에 int:를 붙여서 변수 형태를 integer로 한정시킬 수 있다.
- blog/views.py
 
def index2(request, name):
    return HttpResponse("INDEX2 OK" + name)
def index3(request, name):
    return HttpResponse("INDEX3 OK" + str(pk))
404 Error Exception
- blog/views.py
 
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse
from blog.models import Post
def index3(request, pk):
    #p = Post.objects.get(pk=pk)
    p = get_object_or_404(Post, pk=pk)      #pagenotfound(404) exception return
    return HttpResponse("INDEX3 OK" + p.title)
Post.objects.get(pk=pk) 에서 왼쪽의 pk는 parameter name, 오른쪽의 pk는 variable이다.
get 함수는 pk=pk인 것을 찾아주는 함수이다.
get_object_or_404(Post, pk=pk) : pk=pk인 것을 찾고, 에러 시 아랫줄 return이 아니라 PageNotFound(404) error로 리턴시키도록 exception을 발생시킨다.
List template
- blog/urls.py
 
urlpatterns = [
    ...
    path('list', views.list),
]
- blog/views.py
 
def list(request):
    data = Post.objects.all()
    return render(request, "blog/list.html", {"data":data})
header/footer contents
- templates/blog/base.html
 
{% block content %}
{% endblock %}
부모에게 상속을 받고, 내가 렌더링한 부분은 여기에 넣겠다.
- templates/blog/list.html
 
{% extends 'blog/base.html' %}
{% block content %}
내용
{% endblock %}
{% extends 'blog/base.html' %} 를 base template으로 가져올 건데,
{% block content %} 부터 {% endblock %} 까지 렌더링한다.
LAST CODE
- blog/urls.py
 
from django.urls import path
from . import views     #from 다음에는 폴더명. import 다음에는 함수명이나
urlpatterns = [
    path('', views.index),
    path('<name>/', views.index2),      #<name> parameter에 대해 동적으로 mapping
    path('list', views.list),
    path('<int:pk>/detail', views.detail),
]
- blog/views.py
 
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse
from blog.models import Post
# Create your views here.
def index(request):
    return HttpResponse("INDEX. OKOKOK")
def index2(request, name):
    return HttpResponse("INDEX2 OK" + name)
def list(request):
    data = Post.objects.all()
    return render(request, "blog/list.html", {"data":data})
def detail(request, pk):
    p = get_object_or_404(Post, pk=pk)      #pagenotfound(404) exception return
    return render(request, "blog/detail.html", {"d":p})
- templates/blog/base.html
 
<h1><font color="red">My Blog</font></h1>
<!--부모에게 상속을 받고, 내가 렌더링한 부분은 여기에 넣겠다.-->
{% block content %}
{% endblock %}
<br><br><br>
copy right........<br>
서울특별시........
- templates/blog/list.html
 
{% extends 'blog/base.html' %}
{% block content %}
    {% for d in data %}
        <a href="{{d.pk}}/detail">{{d.title}}</a>
        <hr>
    {% endfor %}
{% endblock %}
- templates/blog/detail.html
 
{% extends 'blog/base.html' %}
{% block content %}
    <h2>게시물 보기</h2>
    title >> {{d.title}}<hr>
    {{d.text|linebreaks}}
    <br><a href="/blog/list">뒤로가기</a>
{% endblock %}
{{변수|linebreaks}} 옵션을 주면 엔터 반영되어 렌더링된다.
in APP - Class type View 적용
class와 객체 이용한 일반적인 로그인 폼
클래스형 뷰 이론
목적: GET method와 POST method를 다른 페이지로 적용할 때, 편리를 위해 Class type View를 사용한다.
- 클래스로 작성되어 있는 뷰 객체를 말한다.
 - 상속과 믹스인 기능 사용으로 코드의 재사용이 가능
 - 뷰의 체계적 관리
 - 제네릭 뷰 작성
 
#urls.py
urlpatterns = [
    path('login/', views.LoginView.as_view())
]
클래스형 뷰는 클래스로 진입하기 위한 진입 메소드를 제공하는데, 이것이 위의 as_view()메소드이며, 아래의 순서로 요청을 처리한다.
as_view()메소드에서 클래스의 인스턴스를 생성한다.- 생성된 인스턴스의 
dispatch()메소드를 호출한다. dispatch()메소드는 요청을 검사해서 HTTP의 메소드(GET, POST)를 알아낸다.- 인스턴스 내에 해당 이름을 갖는 메소드로 요청을 중계한다.
 - 해당 메소드가 정의되어 있지 않으면, HttpResponseNotAllowd 예외를 발생시킨다.
 
#views.py
from django.views.generic import View
class PostView(View):
    def get(self, request):
        return HttpResponse("get OK")
    def post(self, request):
        return HttpResponse("post OK")
- PostView 클래스는 View 클래스를 상속받는다.
 - View 클래스에는 
as_view()메소드와dispatch()메소드가 정의되어 있다. 
함수형 뷰와 비교했을 때 클래스형 뷰가 가지는 장점
- GET, POST 등의 HTTP 메소드에 따른 처리를 메소드명으로 구분 할 수 있어, 좀 더 깔끔한 구조의(IF 문이 없는) 코드를 생산할 수 있다.
 - 다중 상속과 같은 객체 지향 기술이 가능하여 코드의 재사용성이나 개발 생산성을 높여준다.
 
#함수형 뷰에서의 메소드 구분
def my_view(request):  
    if request.method == 'GET':
        # 뷰 로직 작성
        return HttpResponse('result')
함수형 뷰에서는 예제에서 볼 수 있듯 HTTP 메소드별 다른 처리가 필요할 때 if 문을 이용해야 한다. 하지만, 클래스형 뷰는 다음과 같이 코드의 구조가 훨씬 깔끔해진다.
#클래스형 뷰에서의 메소드 구분
from django.views.generic import View
class PostView(View):
    def get(self, request):
        return HttpResponse("get OK")
    def post(self, request):
        return HttpResponse("post OK")
클래스형 뷰에서는 HTTP 메소드 이름으로 클래스 내의 메소드를 정의하면 된다.
단, 메소드명은 소문자로~ 이러한 처리가 가능한 것은 내부적으로 dispatch() 메소드가 어떤 HTTP 메소드로 요청되었는지 알아내고, 이를 처리해주기 때문이다.
상속 기능 가능
개발자가 작성하는 대부분의 클래스형 뷰는 장고가 제공해주는 제네릭 뷰를 상속받아 작성한다.
제네릭 뷰: 뷰 개발 과정에서 공통적으로 사용할 수 있는 기능들을 추상화하고, 장고에서 기본적으로 제공해주는 클래스형 뷰
위에서의 View는 특별한 로직이 없고, URL 맞춰 해당 템플릿 파일의 내용만 보여줄 때 사용하는 제네릭 뷰이기 때문에 위처럼 상속받아 불러오는 것만으로도 사용할 수 있는 것이다.
Django 의 제네릭 뷰
Django 에서 제공하는 제네릭 뷰는 다음과 같이 4가지로 분류할 수 있다.
- Base View: 뷰 클래스를 생성하고, 다른 제네릭 뷰의 부모 클래스를 제공하는 기본 제네릭 뷰
 - Generic Display View: 객체의 리스트를 보여주거나, 특정 객체의 상세 정보를 보여준다.
 - Generic Edit View: 폼을 통해 객체를 생성, 수정, 삭제하는 기능을 제공한다.
 - Generic Date View: 날짜 기반 객체의 년/월/일 페이지로 구분해서 보여준다.
 
아래는 위 4가지 분류에 따른 구체 뷰 클래스에 대한 설명이다.
- Base View
- View: 가장 기본이 되는 최상위 제네릭 뷰
 - TemplateView: 템플릿이 주어지면 해당 템플릿을 렌더링한다.
 - RedirectView: URL이 주어지면 해당 URL로 리다이렉트 시켜준다.
 
 - Generic Display View
- DetailView: 객체 하나에 대한 상세한 정보를 보여준다.
 - ListView: 조건에 맞는 여러 개의 객체를 보여준다.
 
 - Generic Edit View
- FormView: 폼이 주어지면 해당 폼을 보여준다.
 - CreateView: 객체를 생성하는 폼을 보여준다.
 - UpdateView: 기존 객체를 수정하는 폼을 보여준다.
 - DeleteView: 기존 객체를 삭제하는 폼을 보여준다.
 
 - Generic Date View
- YearArchiveView: 년도가 주어지면 그 년도에 해당하는 객체를 보여준다.
 - MonthArchiveView: 월이 주어지면 그 월에 해당하는 객체를 보여준다.
 - DayArchiveView: 날짜가 주어지면 그 날짜에 해당하는 객체를 보여준다.
 
 
제네릭 뷰의 전체 리스트는 여기에서 확인 가능하다.
클래스형 뷰에서의 폼 처리
- blog/views.py
 
from django.forms import Form, CharField, Textarea
class PostForm(Form):
    title = CharField(label='제목', max_length=20)
    text = CharField(label='내용', widget=Textarea)
class PostEditView(View):
    def get(self, request, pk):     #특정 포스트를 수정하므로 pk parameter를 받아와야 한다.
        #초기값 지정
        post = get_object_or_404(Post, pk=pk)
        form = PostForm(initial={'title':post.title, 'text':post.text})
        return render(request, "blog/edit.html", {'form':form})
    def post(self, request, pk):
        form = PostForm(request.POST)
        post = get_object_or_404(Post, pk=pk)
        post.title = form['title'].value()
        post.text = form['text'].value()
        post.publish()
        return redirect('list')
initial={dictionary}
- templates/blog/edit.html
 
{{ form.as_p }}
- form_class: 사용자에 보여줄 폼을 정의한 forms.py 파일 내의 클래스명
 - template_name: 폼을 포함하여 렌더링할 템플릿 파일 이름
 - success_url: MyFormView 처리가 정상적으로 완료되었을 때 리다이렉트 될 URL
 - form_valid() 함수: 유효한 폼 데이터로 처리할 로직 코딩. 반드시 super() 함수를 호출해야 함.
 
참고: http://ruaa.me/django-view/
- 폼 기능을 추가한 blog/views.py
 
from django.forms import Form, CharField, Textarea, ValidationError
def validator(value):
    if len(value) < 5 : raise ValidationError("길이가 너무 짧아요");
class PostForm(Form):
    title = CharField(label='제목', max_length=20, validators=[validator])
    text = CharField(label='내용', widget=Textarea)
class PostEditView(View):
    def get(self, request, pk):     #특정 포스트를 수정하므로 pk parameter를 받아와야 한다.
        #초기값 지정
        post = get_object_or_404(Post, pk=pk)
        form = PostForm(initial={'title':post.title, 'text':post.text})
        return render(request, "blog/edit.html", {'form':form, 'pk':pk})
    def post(self, request, pk):
        form = PostForm(request.POST)
        if form.is_valid():
            post = get_object_or_404(Post, pk=pk)
            post.title = form['title'].value()
            post.text = form['text'].value()
            post.publish()
            return redirect('list')
        return render(request, 'blog/edit.html', {'form':form, 'pk':pk})
Authentication
global settings에 정의된 인증관련 기본 설정에 AUTH_USER_MODEL = 'auth.User' 로 정의되어있다.
#in Jupyter Notebook for Unit Test
from django.contrib.auth import authenticate
user = authenticate(username = 'home', password='choikt1234')
if user == None : print(user)
  from django.contrib.auth import authenticate
user = authenticate(username = 'home', password='choikt1234')
if user == None : print(user)        #None
User 모델 클래스 획득 방법
- 직접 User 모델 import (비추)
 
from django.contrib.auth.models import User
User.objects.all()
global settings 오버라이딩을 통해서 인증 User 모델을 다른 모델로 변경할 수 있음
get_user_modelhelper 함수를 통해 모델 클래스 참조 (추천)
from django.contrib.auth import get_user_model
User = get_user_model()
User.objects.all()
settings.AUTH_USER_MODEL을 통한 모델클래스 참조 (추천)
from django.conf import settings # 추천!
from django.conf.auth.models import User # 비추
from django.db import models
class Post(models.Model):
    author = models.ForeignKey(User)         # 비추
    author = models.ForeignKey('auth.User') # 비추
    author = models.ForeignKey(settings.AUTH_USER_MODEL) # 추천!view에서 현재 로그인 유저 획득하는 방법
- FBV : request.user
 - CBV : self.request.user
- 로그인 상태 :settings.AUTH_USER_MODEL 클래스 인스턴스
 - 로그아웃 상태 :django.contrib.auth.models.AnonymousUser 클래스 (모델 인스턴스가 아님, 다른 모델과 관계 불가능)
 - context_processor를 통해서 user가 모든 view에 context로 기본 제공 됨
 
 
출처: https://wayhome25.github.io/django/2017/05/18/django-auth/
경로 찾아주는 template 명령어
views 사용때문에 생기는 상대경로 문제를 해결해주는 방법이다.
html에 다음과 같은 함수로 상대경로 문제를 해결할 수 있다.
{% url 'url_name' param1 param2 param3 %}
- blog/urls.py
 
urlpatterns = [
    path('', views.index),
    path('list', views.list, name='list'),
    path('<int:pk>/detail', views.detail, name='detail'),       #function base
    path('list2', views.PostView.as_view()),                    #class base
    path('login/', views.LoginView.as_view(), name='login'),
]
path에 name을 정의하게되면, name으로 경로명을 자동으로 알아낼 수 있다.
- blog/views.py
 
...
            return redirect('login')    #urls.py에서 지정한 name. NOT 경로명.
...
        return redirect('list')
urls.py에서 login이라는 name을 지정했으므로, 경로명이 아니라 name을 불러온 것이다.
- templates/blog/login.html
 
<form action="{% url 'login' %}" method="post">
{% url 'login' %} : url 명령어 뒤 name을 알아서 절대경로를 찾아 지정해준다.
- templates/blog/list.html
 
    {% for d in data %}
        <a href="{% url 'detail' d.pk %}">{{d.title}}</a>
        <hr>
    {% endfor %}
{% url 'detail' d.pk %} : url 명령어 뒤 name=detail, parameter=d.pk 를 가져와 절대경로를 찾아 지정해준다.
cf) in templates,
명령어 사용할 때에는 {% 명령어%}
변수값을 가져올 때에는 {{변수명}}
여러 가지 경로 지정 방법 비교
- CODE of list.html
 
{% extends 'blog/base.html' %}
{% block content %}
    {% for d in data %}
        <a href="{% url 'detail' d.pk %}">{{d.title}}</a><br>
        <a href="/blog/{{d.pk}}/detail">{{d.title}}</a><br>
        <a href="/blog/detail?id={{d.pk}}">{{d.title}}</a><br>
        <a href="detail?id={{d.pk}}">{{d.title}}</a><br>
        <hr>
    {% endfor %}
{% endblock %}
첫번째 방법: 경로 찾아주는 template 명령어
두번째 방법: 절대경로
세번째: get parameter로 받아올 경우, 절대경로
네번째: get parameter로 받아올 경우, 상대경로
<a href="/blog/1/detail">오늘 날씨가 추워요</a><br>
<a href="/blog/1/detail">오늘 날씨가 추워요</a><br>
<a href="/blog/detail?id=1">오늘 날씨가 추워요</a><br>
<a href="detail?id=1">오늘 날씨가 추워요</a><br>
LAST CODE
blog/models.py
from django.db import models
class User(models.Model) :
    userid = models.CharField(max_length=10, primary_key=True)
    name = models.CharField(max_length=10)
    age = models.IntegerField()
    hobby = models.CharField(max_length=20)
    def __str__(self):                          #print 적용할 때 자동으로 적용되는 함수
        return f"{self.userid} / {self.name} / {self.age}"
blog/urls.py
from django.urls import path
from . import views     #from 다음에는 폴더명. import 다음에는 함수명이나
urlpatterns = [
    path('', views.index),
#    path('<name>/', views.index2),      #<name> parameter에 대해 동적으로 mapping
#    path('<int:pk>/detail', views.index3),
    path('login/', views.LoginView.as_view(), name='login'),    # class base
    path('list/', views.list, name='list'),
    path('<int:pk>/detail/', views.detail, name='detail'),       #function base
    path('add/', views.PostView.as_view(), name='add'),
    path('<int:pk>/edit/', views.PostEditView.as_view(), name='edit'),
]
login
- blog/views.py
 
from django.shortcuts import render, redirect, get_object_or_404
from django.http import HttpResponse
from blog.models import Post
from django.views.generic import View
from django.contrib.auth import authenticate
from django.contrib.auth.models import User
from django.forms import Form, CharField, Textarea, ValidationError
class LoginView(View):
    def get(self, request):
        return render(request, "blog/login.html")
    def post(self, request):
        #Loging 처리
        username = request.POST.get('username')
        password = request.POST.get('password')
        user = authenticate(username=username, password=password)
        if user == None :
            return redirect('login')    #urls.py에서 지정한 name. NOT 경로명.
        #로그인 성공한 경우
        request.session['username'] = username
        return redirect('list')
- templates/blog/login.html
 
<form action="{% url 'login' %}" method="post">    <!--action 빼면 자기 자신, url이라는 명령어 뒤 name-->
{% csrf_token %}
    username <input type="text" name="username"><br>
    password <input type="password" name="password"><br>
    <input type="submit" value="로그인">
</form>
base.html
<h1><font color="red">My Blog</font></h1>
로그인 사용자: {{username}} 님<br>
<!--부모에게 상속을 받고, 내가 렌더링한 부분은 여기에 넣겠다.-->
{% block content %}
{% endblock %}
<br><br><br>
copy right........<br>
서울특별시........
list
- blog/views.py
 
def list(request):
    username = request.session['username']              #text
    user = User.objects.get(username=username)          #object
    data = Post.objects.all().filter(author=user)
    context = {"data":data, 'username':username}
    return render(request, "blog/list.html", context)
- templates/blog/list.html
 
{% extends 'blog/base.html' %}
{% block content %}
    <a href="{% url 'add' %}">글쓰기</a><br>
    <h2>글 리스트</h2>
    {% for d in data %}
        <a href="{% url 'detail' d.pk %}">{{d.title}}</a><br>
        <hr>
    {% endfor %}
{% endblock %}
detail
- blog/views.py
 
def detail(request, pk):
    p = get_object_or_404(Post, pk=pk)      #에러나면 아래 return이 아닌, pagenotfound(404) exception로 리턴시킨다.
    return render(request, "blog/detail.html", {"d":p})
- templates/blog/detail.html
 
{% extends 'blog/base.html' %}
{% block content %}
    <h2>게시물 보기</h2>
    {{d.title}}<hr>
    {{d.text|linebreaks}}
    <br><a href="{% url 'edit' d.pk %}">수정</a>
    <br><a href="/blog/list">목록 보기</a>
{% endblock %}
add
- blog/views.py
 
class PostView(View):
    def get(self, request):
        username = request.session['username']
        return render(request, "blog/add.html", {'username':username})
    def post(self, request):
        title = request.POST.get('title')
        text = request.POST.get('text')
        username = request.session['username']
        user = User.objects.get(username=username)
        Post.objects.create(title=title, text=text, author=user)    #create:생성과 동시에 save
        return redirect('list')
- templates/blog/add.html
 
{% extends 'blog/base.html' %}
{% block content %}
    <form action="{% url 'add' %}" method="post">
    {% csrf_token %}
        제목  <input type="text" name="title" /><br>
        내용  <textarea rows="10" cols="30" name="text"></textarea>
        <input type="submit" value="작성">
    </form>
{% endblock %}
edit
데이터를 읽어와서 default value로 넣어줘야 한다.
- blog/views.py
 
def validator(value):
    if len(value) < 5 : raise ValidationError("길이가 너무 짧아요");
class PostForm(Form):
    title = CharField(label='제목', max_length=20, validators=[validator])
    text = CharField(label='내용', widget=Textarea)
class PostEditView(View):
    def get(self, request, pk):     #특정 포스트를 수정하므로 pk parameter를 받아와야 한다.
        #초기값 지정
        post = get_object_or_404(Post, pk=pk)
        form = PostForm(initial={'title':post.title, 'text':post.text})
        return render(request, "blog/edit.html", {'form':form, 'pk':pk})
    def post(self, request, pk):
        form = PostForm(request.POST)
        if form.is_valid():
            post = get_object_or_404(Post, pk=pk)
            post.title = form['title'].value()
            post.text = form['text'].value()
            post.publish()
            return redirect('list')
        return render(request, 'blog/edit.html', {'form':form, 'pk':pk})
- templates/blog/edit.html
 
{% extends 'blog/base.html' %}
{% block content %}
    <form action="{% url 'edit' pk %}" method="post">
    {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value="작성">
    </form>
{% endblock %}'Python > Django' 카테고리의 다른 글
| 장고(django)에서 이미지 업로드 필드 추가 없이 (0) | 2020.04.24 | 
|---|---|
| Django에 Database 사용하기 (1) | 2020.02.19 | 
| HTML과 python web server 구축 (0) | 2020.02.18 | 
| Python Socket programming (0) | 2020.02.18 | 
| OSI 참조 모델 이론 (0) | 2020.02.18 |