ORM
Admin Interface
Authentication
internationalization
URL Routing
Template Engin
사용 이유 Flask 나 FastAPI 는 여러개 패키지를 가져다 써야하는데 모두 준비가 되있음
개발 시간을 줄여주는 관리자 페이지의 마법
개발 시간 및 코드를 줄여주는 모듈화의 마법
확일화된 구조로 누가 코드를 봐도 적응시간 빠름
수많은 패키지와 잘 구축된 커뮤니티
poetry : poetry는 Python의 의존성 관리와 패키지 관리를 쉽게 해주는 도구입니다. 이 명령어는 새로운 프로젝트를 시작할 때, 프로젝트의 메타데이터와 의존성 관리 파일을 생성합니다.
Model:DB 관련 알고리즘 View:메인알고리즘 Template: 화면단
장점 : 개발속도가 빠름, 코드재사용 및 모듈화 : 유연, 안전한 웹 애플리케이션 구축 : 보안
단점 : 잘 쓰려면 숙련 및 개념 탑재 필요
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [BASE_DIR/ 'templates'],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
urls.py
return render(request, 'xx.html', context)
db browser for sqlite
블로그 모델 만들시
class 테이블명(models.Model):
관리자단 페이지에서 하나씩 수정할 수 있는 기능
# admin.py admin 페이지에서 관리할 모델 임포트
from 앱.models import 모델명
admin.site.register(모델명)
from django.contrib import admin
from todo.models import Todo
# Register your models here.
@admin.register(Todo)
class TodoAdmin(admin.ModelAdmin):
list_display = ( # 목록 페이지의 열으로 표시할 필드 지증
'title',
'description',
'is_complete',
'start_date',
'end_date',
)
list_filter = ('is_complete',) # 필터링 옵션
search_fields = ('title',) # 검색 창에서 검색 가능한 필드 지정
ordering = ('start_date',)
fieldsets = ( # 데이터 입력 페이지의 레이아웃을 커스터마이징, 섹션별로 나누어 표시
('Todo Info', {
'fields': ('title', 'description', 'is_complete',)
}),
('Date Range', {
'fields': ('start_date', 'end_date',)
}),
)
from django.contrib import admin
from bookmark.models import Bookmark
class BookmarkAdmin(admin.ModelAdmin):
list_display = ['name', 'url'] # 이부분이 위에 표시됨 컬럼이 추가되는 느낌?
list_display_links = ['name', 'url'] # 클릭 가능하게
admin.site.register(Bookmark, BookmarkAdmin) # 추가로 등록 해줘야함
bookmarks = Bookmark.objects.all()
# SELECT * FROM bookmark
bookmark = Bookmark.objects.get(pk=pk)
# SELECT * FROM bookmark WEHER id=id LIMIT 1
Bookmark : [Bookmark] = Bookmark.objects.filter(name='네이버')
# SELECT * FROM bookmark WHERE name ='네이버'
now = datetime.now()
Bookmark : [Bookmark] = Bookmark.objects.filter(created_at__gte = now)
# gte gratter than equal >= , gt >
# SELECT * FROM bookmark WHERE created_at > now
# lte, lt >= now
Bookmark : [Bookmark] = Bookmark.objects.filter(nae__icontains='네이')
# SELECT * FROM bookmark WHERE name LIKE '%네이%'
In [18]: Bookmark.objects.filter(name__startswith='다')
Out[18]: <QuerySet [<Bookmark: 다음 https://daum.net>]>
In [18]: Bookmark.objects.filter(name__endswith='음')
Out[18]: <QuerySet [<Bookmark: 다음 https://daum.net>]>
In [21]: Bookmark.objects.filter(name__in=['다음','네'])
Out[21]: <QuerySet [<Bookmark: 다음 https://daum.net>]>
In [22]: Bookmark.objects.filter(name='네이버',url__startswith='https://naver')
Out[22]: <QuerySet [<Bookmark: 네이버 https://naver.com>]
In [24]: Bookmark.objects.create(name='야후', url="https://yahoo.com")
Out[24]: <Bookmark: 야후 https://yahoo.com>
In [28]: bookmark = Bookmark(name='야후2', url='https://yahoo.com')
In [29]: bookmark
Out[29]: <Bookmark: 야후2 https://yahoo.com>
# 아직 생성 하지 않음
In [30]: bookmark.save() # 생성
In [32]: b = _.first() # 마지막 아웃풋 값의 첫번째값을 가져와라
# _ : 전 아웃풋 전부 가져오는것 objects.all()
In [33]: b
Out[33]: <Bookmark: 네이버 https://naver.com>
In [33]: b
Out[33]: <Bookmark: 네이버 https://naver.com>
In [34]: b.id
Out[34]: 2
In [35]: b.id = None
In [36]: b.save() # 장고에서 id 값을 마지막에 auto_increment 해서 추가함
# 장고에서는 id 값이 있냐 없냐에 따라 이 객체가 db에 있냐 없냐 판단하기에 중요한 값임
In [37]: b.id
Out[37]: 8
bookmark_list = [Bookmark(name=f" 테스트 구글 {i}", url=f'https://google.com') for i in range(10)]
Bookmark.objects.bulkcreate(bookmark_list)
In [39]: Bookmark.objects.filter(url__icontains='naver.com')
Out[39]: <QuerySet [<Bookmark: 네이버 https://naver.com>, <Bookmark: 네이버 https://naver.com>]>
In [40]: Bookmark.objects.filter(url__icontains='naver.com').update(name='Naver')
Out[40]: 2 # 3개가 업데이트 되었다.
In [41]: Bookmark.objects.filter(url__icontains='naver.com')
Out[41]: <QuerySet [<Bookmark: Naver https://naver.com>, <Bookmark: Naver https://naver.com>]>
In [42]: b
Out[42]: <Bookmark: 네이버 https://naver.com>
In [43]: b.id
Out[43]: 8
In [44]: b.delete()
Out[44]: (1, {'bookmark.Bookmark': 1})
In [45]: b
Out[45]: <Bookmark: 네이버 https://naver.com>
In [46]: b.id # 장고에서 id 값이 없으면 db 에 없는 것임
In [47]: Bookmark.objects.all()
Out[47]: <QuerySet [<Bookmark: Naver https://naver.com>, <Bookmark: 다음 https://daum.net>, <Bookmark: 네이버 https://m.mobile.com>, <Bookmark: 야후 >, <Bookmark: 야후 https://yahoo.com>, <Bookmark: 야후2 https://yahoo.com>]>
In [48]: Bookmark.objects.filter(name__icontains='야후')
Out[48]: <QuerySet [<Bookmark: 야후 >, <Bookmark: 야후 https://yahoo.com>, <Bookmark: 야후2 https://yahoo.com>]>
In [49]: Bookmark.objects.filter(name__icontains='야후').delete()
Out[49]: (3, {'bookmark.Bookmark': 3})
In [50]: Bookmark.objects.all()
Out[50]: <QuerySet [<Bookmark: Naver https://naver.com>, <Bookmark: 다음 https://daum.net>, <Bookmark: 네이버 https://m.mobile.com>]>
from django.db import models
# models.py 구현
# Create your models here.
# 제목
# 본문
# 작성자
# 수정일자
# 카테고리
class Blog(models.Model):
CATEGORY_CHOICES = (
# db에 들어갈 글자 : 실제로 보여질 글자
('free', '자유'),
('travel', '여행'),
('cat', '고양이'),
('dog', '강아지'),
)
# enum 같은 역할 charfield 인데도 불구하고 고류는 걸로 나오게함
category = models.CharField('카테고리', max_length=20, choices = CATEGORY_CHOICES)
title = models.CharField('제목', max_length=100)
content = models.TextField('본문')
created_at = models.DateTimeField('작성일자', auto_now_add=True)
updated_at = models.DateTimeField('수정일자', auto_now=True)
# admin.py
from django.contrib import admin
from .models import Blog
# Register your models here.
@admin.register(Blog)
class BlogAdmin(admin.ModelAdmin):
...
path('blog/detail/<int:pk>/', views.blog_detail, name = "blog_detail"), # 네임에 해당하는 url path 경로를 잡아주기 위해 사용
<h1>블로그 목록</h1>
{% for blog in blogs%}
<p>
<!-- 이름이 blog_detail 인 것의 path 로 가겠다. id 값은 저거고 -->
<a href="{% url 'blog_detail' blog.pk %}">
{{ blog.title }} - {{ blog.created_at|date:"Y-m-d" }}
</a>
</p>
{% endfor %}
사용자 행동에 대한 정보를 기억하기 위해 필요
웹은 기본적으로 상태가 없는 stateless 임 그래서 매번의 요청이 독립적이여서 브라우저는 기억하고 있어야 할 정보를 같이 보내는 작업이 필요함(로그인 같은),
그래서 보낼떄 쿠키에 담아서 보냄
Cookie : 정보를 유저단에 저장함, 브라우저에 저장되기에 보안에 취약함
session : 기본적으로 쿠키에 유사하지만,브라우저에서 보안상의 이유로 갖고있을 수 없는 것들을 가지고 있음
항목 | 쿠키 | 세션 |
---|---|---|
저장 위치 | 클라이언트 | 서버 |
저장 형식 | 텍스트 | 객체 형태 |
종료 시점 | 설정한 시간 (없으면 브라우저 종료 시) | 정확한 종료 시점 알 수 없음 |
자원 사용 | 클라이언트 자원 | 서버의 자원 |
용량 제한 | 한 도메인당 20개, 쿠키당 4KB, 총 300개 | 서버가 허용하는 한 제한 없음 |
from django.shortcuts import render , get_object_or_404
from .models import Blog
# Create your views here.
def blog_list(request):
blogs = Blog.objects.all()
# 리턴값은 string 방문자수 는 1이였으면 2가 됨
visits = int(request.COOKIES.get('visits', 0)) + 1 # get 은 뒤에 key 값을 가져오는데 혹시 없으면 defaul로 지정해 논 0을 가져옴
request.session['count'] = request.session.get('count',0) + 1
context = {
'blogs': blogs,
'count' : request.session['count']
}
response = render(request, 'blog_list.html', context)
response.set_cookie('visits', visits)
return response
# html 에 추가 <h1>{{ count }}</h1>
Django autentication, Autentication views 활용해서 제작
<!-- login.html -->
<body>
<form method="POST">
{% csrf_token %}
{{ form.as_p }}
<button>submit</button>
</form>
</body>
<h3>{{ request.user.username }}</h3> <!-- 잘적용되 있는지 확인 -->
<div style ="text-align: right">
<a href="{% url 'login' %}">로그인</a>
{# <a href="{% url 'logout' %}">로그아웃</a> <!-- 현재 logout은 get요청으로 들어간다 그래서 이걸 형식을 바꿔줘야 작동한다 -->#}
<form action="{% url 'logout' %}" method="POST" style="display: inline">
{% csrf_token %}
<button>로그아웃</button>
<h3>{{ request.user.username }}</h3> <!-- 잘적용되 있는지 확인 -->
</form>
</div>
{% if request.user.is_authenticated %}
{# <a href="{% url 'logout' %}">로그아웃</a> <!-- 현재 logout은 get요청으로 들어간다 그래서 이걸 형식을 바꿔줘야 작동한다 -->#}
<form action="{% url 'logout' %}" method="POST" style="display: inline">
{% csrf_token %}
<button>로그아웃</button>
<h3>{{ request.user.username }}</h3> <!-- 잘적용되 있는지 확인 -->
{% else %}
<a href="{% url 'login' %}">로그인</a>
{% endif %}
# urls.py
from django.contrib import admin
from django.urls import path, include
from blog import views
from member import views as member_views
urlpatterns = [
path('admin/', admin.site.urls),
path('', views.blog_list, name = "blog_list"),
path('blog/detail/<int:pk>/', views.blog_detail, name = "blog_detail"), # 네임에 해당하는 url path 경로를 잡아주기 위해 사용
path('accounts/', include("django.contrib.auth.urls")), # 이걸해줘야 logout path도 자동으로 인식됨
path('signup/', member_views.sign_up, name = 'signup'),
path('login/', member_views.login, name = 'login'),
]
<!-- login.html 생성후 제작 POST 보내는 폼 -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="POST">
<!-- csrf 가 없으면 서버에서 거부함 -->
{% csrf_token %} <!-- 클라이언트와 서버가 공유되고 있는 인증 값임, settings 에 보면 middleware.csrf가 있을거임, 그래서 장고의 모든 post 요청에는 이런것을 검증하는 로직이 있을거임 -->
{{ form.as_p }}
<button>submit</button>
</form>
</body>
</html>
<!-- blog_list 페이지 수정 -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<nav>
<div style ="text-align: right">
{% if request.user.is_authenticated %}
{# <a href="{% url 'logout' %}">로그아웃</a> <!-- 현재 logout은 get요청으로 들어간다 그래서 이걸 형식을 바꿔줘야 작동한다 -->#}
<form action="{% url 'logout' %}" method="POST" style="display: inline">
{% csrf_token %}
<button>로그아웃</button>
<h3>{{ request.user.username }}</h3> <!-- 잘적용되 있는지 확인 -->
{% else %}
<a href="{% url 'login' %}">로그인</a>
{% endif %}
</div>
</nav>
<h1>블로그 목록</h1>
<h3>{{ request.user.username }}</h3> <!-- 잘적용되 있는지 확인 -->
<h1>{{ count }}</h1> <!-- 이렇게 출력해 주지 않으면 클라이언트가 알 수가 없음 -->
{% for blog in blogs%}
<p>
<a href="{% url 'blog_detail' blog.pk %}">
{{ blog.title }} - {{ blog.created_at|date:"Y-m-d" }}
</a>
</p>
{% endfor %}
</body>
</html>
모듈화, 어디서든 쓰일 수 있게 제작
from django.contrib.auth.forms import UserCreationForm
form = UserCreationForm()
context = {
'form' : form
}
생성 및 signup.html 작성
<h1>회원가입</h1>
<form method="POST">
{% csrf_token %}
{{ form.as_p }} <!-- 로그인 페이지에는 장고에서 기본적으로 제공되는 views 가있어서 이렇게 바로 쓸 수 있지만 우리가 만드는페이지에서는 아직 정의 되지 않아서 사용 불가능 하다 -->
<button>회원가입</button>
username= request.POST['username']
password1 = request.POST['password1']
password2 = request.POST['password2']
username= request.POST.get('username') # get은 있으면 받고 없으면 넘긴다
password1 = request.POST.get('password1')
password2 = request.POST.get('password2')
if request.method =='POST':
form = UserCreationForm(request.POST) # 넣어주고 확인만 해주면 됨
if form.is_valid():
form.save()
return redirect('/accounts/login') # django.shortcut 에서 임포트하면됨
else :
form = UserCreationForm # get 요청일때도 써야하니까
<a href="{% url 'signup' %}">회원가입</a>
form = UserCreationForm(request.Post or None) # 이걸 사용 하면 if request.method =="post") 이걸 삭제 시킬수 있음
if form.is_valid():
form.save()
return redirect('/accounts/login') # django.shortcut 에서 임포트하면 됨
if form.is_valid():
form.save()
from django.conf import settings # 장고의 config 에서 가져오니까 나중에 config 이름이 바껴도 오류를 발생시키지 않고 가져옴
return redirect(settings.LOGIN_URL) # django.shortcut 에서 임포트하면됨
def login(request):
form = AuthenticationForm(request, request.POST or None)
if form.is_valid():
from django.contrib.auth import login as django_login
django_login(request, form.get_user())
# return redirect('/') # 가능
from django.urls import reverse
return redirect(reverse('blog_list')) # config url에 있는 name 에 해당하는 것을 보냄
context = {
'form' : form
}
return render(request, 'registration/login.html', context)
from django.contrib.auth import get_user_model # 장고에 어떤것이 연결되어 있는지 확인한 후 가져오는 것이 이 함수
# 장고에 기본 설정에 있는 user 를 가져오는 것임
User = get_user_model()
# 블로그 클래스 안에
author = models.ForeginKey(User)
author = models.ForeignKey(User, on_delete=models.CASCADE) # 유저가 삭제되면 같이 삭제되는게 블로그
# ForeignKey 같은 경우는 on_delete를 사용해야 함
# models.CASCADE => 같이 삭제
# models.PROTECT => 삭제가 불가능함 (유저를 삭제하려고 할 떄 블로그가 있으면 유저 삭제가 불가능)
# models.SET_NULL => 널값을 넣음 (유저 삭제시 블로그의 author가 null이 됨
<h1>{{ blog.title }}</h1>
<div style="text-align: right">
{{ blog.author.username }}
</div>
<p>{{ blog.content }}</p>
<a href ="{% url 'blog_list' %}">목록으로 돌아가기</a>
공통적인 부분 적용시켜 필요한 부분만 제작하기
{% block content %}{% endblock %}
<!-- 공통 부분 -->
<body>
<nav>
<div style ="display: flex; justify-content: space-between">
<a href="{% url 'blog_list' %}">홈</a>
</div>
<div style ="text-align: right">
{% if request.user.is_authenticated %}
{# <a href="{% url 'logout' %}">로그아웃</a> <!-- 현재 logout은 get요청으로 들어간다 그래서 이걸 형식을 바꿔줘야 작동한다 -->#}
<form action="{% url 'logout' %}" method="POST" style="display: inline">
{% csrf_token %}
<button>로그아웃</button>
<h3>{{ request.user.username }}</h3> <!-- 잘적용되 있는지 확인 -->
{% else %}
<a href="{% url 'signup' %}">회원가입</a>
<a href="{% url 'login' %}">로그인</a>
{% endif %}
</div>
</nav>
<!-- 각자의 사용 부분 -->
{% block content %}{% endblock %} <!-- 컨텐트라는 구멍을 뚫어놓음 -->
</body>
{% extends 'base.html' %}
{% block content %}
<h1>{{ blog.title }}</h1>
<div style="text-align: right">
{{ blog.author.username }}
</div>
<p>{{ blog.content }}</p>
<a href ="{% url 'blog_list' %}">목록으로 돌아가기</a>
{% endblock %}
Django의 Form은 사용자로부터 데이터를 수집하고 처리하는 데 사용되는 도구로, HTML 폼을 쉽게 생성하고 데이터 유효성을 검사할 수 있도록 도와줍니다. Django의 Form은 HTML 폼을 정의, 폼 데이터를 수집, 검증 및 처리하는 과정을 간소화합니다.
1. HTML 폼 생성:
• Django는 Python 코드로 HTML 폼을 자동으로 생성할 수 있도록 도와줍니다.
• 입력 필드, 라벨, 위젯 등을 쉽게 정의할 수 있습니다.
2. 데이터 검증:
• Form 클래스는 입력받은 데이터가 유효한지 검증하고, 유효하지 않을 경우 에러 메시지를 제공합니다.
• 기본 제공되는 데이터 검증(예: 필수 입력값, 이메일 형식 등) 외에, 커스텀 검증도 가능합니다.
3. 보안:
• Django의 Form은 CSRF(Cross-Site Request Forgery) 보호 기능을 제공합니다.
• 사용자 입력 데이터를 안전하게 처리하고, SQL Injection 등의 공격을 방지합니다.
4. 간편한 데이터 처리:
• 폼 데이터를 정리(clean)하고, Python 객체로 변환하거나 데이터베이스에 저장할 수 있습니다.
from django import forms
from blog.models import Blog
# form을 내 모델의 어떤 테이블과 맞출 건지에 관한 작업
class BlogForm(forms.ModelForm): # 폼의 모델 가져오기
class Meta:
model = Blog
# 어떤필드를 적용 시킬
fields = ('title', 'content') # Blog 모델의 'title'과 'content' 필드만 폼에 포함
# Meta 클래스는 ModelForm의 메타 데이터를 정의하는 부분입니다. 이 클래스는 ModelForm이 어떤 모델과 연결되는지, 어떤 필드를 폼에 포함할지 등의 정보를 제공
# fields = '__all__' # 전체를 다 적용하고 싶을때
def blog_create(request):
form = BlogForm()
context = {
'form' : form
}
return render(request, 'blog_create.html', context)
path('create/', views.blog_create, name = 'blog_create'),
{% extends 'base.html' %}
{% block content %}
<form method ="POST">
<h1>블로그 작성</h1>
{% csrf_token %} <!-- 장고에서 포스트 요청 보낼때 보안기능으로 꼭 해줘야함 -->
{{ form.as_p }}
<button>생성</button>
</form>
{% endblock %}
def blog_create(request):
form = BlogForm(request.POST or None)
if form.is_valid():
blog = form.save(commit=False) # 데이터베이스 저장을 지연
blog.author = request.user # 현재 로그인한 사용자를 author로 설정
blog = form.save()
return redirect(reverse('blog_detail', {'pk':blog.pk}))
context = {
'form' : form
}
return render(request, 'blog_create.html', context)
if not request.user.is_autenticated:
return redirect(reverse('login'))
from django.contrib.auth.decorators import login_required
# 이걸 등록하면 데코레이터로
@loging_required() # 로그인중이 아닐때 생성 하면 로그인 페이지로 돌려보내느 데코레이터 이다, settings.py 에 LOGIN_URL 로 세팅 해놓은 곳으로 보낸다.
def login(request):
form = AuthenticationForm(request, request.POST or None)
if form.is_valid():
django_login(request, form.get_user())
next = request.GET.get('next') # GET['next'] 로 해도 되지만 그렇게 하면 없으면 오류가 난다.
if next:
return redirect(next)
# 이렇게 설정하면 create 로 잘 간다.
수정 버튼은 로그인된 유저와 작성자가 같을때만 보여야함
create 와 detail의 혼합
@login_required()
def blog_update(request, pk):
blog = get_object_or_404(Blog, pk=pk, author=request.user) # 조건이 2가지 들어간 것 아니면 404 페이지
# 밑에꺼나 위에꺼 쓰면됨
# if request.user != blog.author:
# raise Http404
context ={
'blog' : blog
}
return render(request, 'blog_update.html', context)
path('<int:pk>/update', views.blog_update, name='blog_update'),
<!-- 로그인 시에만 보여주는 코드 if -->
{% if request.user == blog.author %}
<div style="text-align: right">
<a href="{% url 'blog_update' blog.pk %}">수정</a>
</div>
{% endif %}
@login_required()
def blog_update(request, pk):
blog = get_object_or_404(Blog, pk=pk, author=request.user) # 조건이 2가지 들어간 것 아니면 404 팅김
# 밑에꺼나 위에꺼 쓰면됨
# if request.user != blog.author:
# raise Http404
form = BlogForm(request.POST or None, instance=blog) # 생성시에는 기본값이 없었으나 폼 자체가 블로그 기반 모델이니까 타이틀이면 타이틀 자동으로 들어가게 됨
if form.is_valid():
blog.save()
return redirect(reverse('blog_detail', kwargs={'pk': blog.pk})) # 딕셔너리로 넣어서 kwargs
context ={
'form' : form, # 여기 있는 form 이 저장된 db 의 form 임
}
return render(request, 'blog_update.html', context)
In [1]: blog_list = Blog.objects.all()
In [2]: new_blog_list = []
In [3]: for i in range(20):
...: for blog in blog_list :
...: blog.id = None # 장고에서 id 값이 없으면 새로운 모델이라 생각하니까 기존의 값을 지우고 none 이라함
...: new_blog_list.append(blog
In[6]:Blog.objects.bulk_create(new_blog_list)
paginator = Paginator(blogs, 10) # 한페이지당 몇개씩 할지
page = request.GET.get('page') # 페이지가 있으면 페이지에 넣어줌
page_object = paginator.get_page(page)
{% endfor %}
<div>
{% if page_object.has_previous %} <!-- 이전테그가 있는지 확인하는 메서드 -->
<a href="?page=1">« 첫번째</a>
<a href="?page={{ page_object.previous_page_number }}">이전</a>
<span>
Page {{ page_object.number }} of {{ page_object.paginator.num_pages }} <!-- 왼쪽 현재 몇번째 페이진지 오른쪽 총페이지 -->
</span>
{% endif %}
{% if page_object.has_next %}
<a href="?page={{ page_object.next_page_number }}">다음</a>
<a href="?page={{ page_object.paginator.num_pages }}">마지막</a>
{% endif %}
</div>
{% extends 'base.html' %}
{% block content %}
<h1>블로그 목록</h1>
<p style="text-align: right">
<a href="{% url 'blog_create' %}">생성</a>
</p>
<h3>{{ request.user.username }}</h3> <!-- 잘적용되 있는지 확인 -->
<h1>{{ count }}</h1> <!-- 이렇게 출력해 주지 않으면 클라이언트가 알 수가 없음 -->
{% for blog in page_object%}
<p>
<a href="{% url 'blog_detail' blog.pk %}">
({{ blog.id }}) {{ blog.title }} <span>{{ blog.author.username }}</span> - {{ blog.created_at|date:"Y-m-d" }}
</a>
</p>
{% endfor %}
<div>
{% if page_object.has_previous %} <!-- 이전테그가 있는지 확인하는 메서드 -->
<a href="?page=1">« 첫번째</a>
<a href="?page={{ page_object.previous_page_number }}">이전</a>
{% endif %}
{% if page_object.number|add:-2 > 1 %}
<a href="?page={{ page_object.number|add:-3 }}">…</a>
{% endif %}
{# 1페이지부터 최대페이지 까지 들어있음 #}
{% for i in page_object.paginator.page_range %}
<!-- 현재페이지 == i 이면 -->
{% if page_object.number == i %}
<span>(현재페이지)</span>
{# 2페이지 -4+i < i < i+4 일때 쓸려는듯 이범위의 페이지만 나오게 #}
{% elif i > page_object.number|add:-3 and i < page_object.number|add:3 %} <!-- 띄워쓰면 오류나니까 잘 붙여쓰자 add -->
<a href="?page={{ i }}">{{ i }}</a>
{% endif %}
{% endfor %}
{# 최대 페이지 #}
{% if page_object.paginator.num_pages > page_object.number|add:2 %}
<a href="?page={{ page_object.number|add:3}}">…</a>
{% endif %}
{% if page_object.has_next %}
<a href="?page={{ page_object.next_page_number }}">다음</a>
<a href="?page={{ page_object.paginator.num_pages }}">마지막 »</a>
{% endif %}
</div>
{% endblock %}
html 부터 작성해야함
{# 검색 폼 #}
<form method="get" style="margin-bottom: 10px">
<input name="q" type="text" placeholder="검색어를 입력하세요">
<button>검색</button>
</form>
q= request.GET.get('q') # 페이지 네이터에 넣을떄 이미 필터링이 진행되어 있어야함
if q : # q 가 있을때만 동작
blogs = blogs.filter(title__icontains=q) # 쿼리셋을 이미 잡은것을 가져온것이기에 Blog 에 filter을 하면 안되고 가져온 값에 해야한다
# 그리고 쿼리를 가져올때는 무조건 objects 를 사용해야 한다.
# 아니면 blog = get_object_or_404(Blog, pk=pk) 이런식
# 본문에서 검색하고 싶으면 content__icontains
# 본문에서 이미 blogs= 을 사용해서 쓰고 있으니까
# blogs 자체를 받아온것이다 blogs=blogs
# 그래야 html 에서 사용되는 것 자체가 수정되서 화면에 나타날 것이다
# context 에 q 를 추가하지 않았지만 사용자로부터 q를 받아오는 것이기에 html 에서
# 사용할 수 있다.
# html 코드에 맞춰서 q를 작성하는 것임
# or 검색도 가능
if q :
# or 검색
from django.db.models import Q
blogs = blogs.filter(
Q(title__icontains=q) | # 논리연산자 반드시 명시
Q(content__icontains=q)
)
# blogs = blogs.filter(title__icontains=q)
### def blog_list(request)
def blog_list(request):
blogs = Blog.objects.all().order_by('-created_at')
q= request.GET.get('q') # 페이지 네이터에 넣을떄 이미 필터링이 진행되어 있어야함
if q :
# or 검색
from django.db.models import Q
blogs = blogs.filter(
Q(title_icontains=q),
Q(content_icontains=q)
)
# blogs = blogs.filter(title__icontains=q)
from django.core.paginator import Paginator
paginator = Paginator(blogs, 10) # 한페이지당 몇개씩 할지
page = request.GET.get('page') # 페이지가 있으면 페이지에 넣어줌
page_object = paginator.get_page(page)
# 리턴값은 string 방문자수 는 1이였으면 2가 됨
visits = int(request.COOKIES.get('visits', 0)) + 1 # get 은 뒤에 key 값을 가져오는데 혹시 없으면 defaul로 지정해 논 0을 가져옴
request.session['count'] = request.session.get('count',0) + 1
context = {
'blogs': blogs,
'count' : request.session['count'],
'page_object' : page_object,
}
response = render(request, 'blog_list.html', context)
response.set_cookie('visits', visits)
return response
<!-- html 코드 -->
{# 검색 폼 #}
<form method="get" style="margin-bottom: 10px">
<input name="q" type="text" placeholder="검색어를 입력하세요" value="{% if request.GET.q %}{{ request.GET.q }}{% endif %}"><!-- 검색한 것이 있으면 안에 들어가 있게 -->
<button>검색</button>
</form>
@login_required()
def blog_delete(request, pk):
blog = get_object_or_404(Blog, pk=pk, author=request.user)
blog.delete()
return redirect(reverse('blog_list'))
path('<int:pk>/delete/', views.blog_delete, name='blog_delete'),
<div style="text-align: right">
<a href="{% url 'blog_update' blog.pk %}">수정</a>
<a href="{% url 'blog_delete' blog.pk %}">삭제</a>
</div>
@login_required()
@require_http_methods(['POST']) # 특정 메소드만 넣을수 있는 기능
def blog_delete(request, pk):
# if request.method != "POST": # 데코레이터 썻기에 안써도댐
# raise Http404
blog = get_object_or_404(Blog, pk=pk, author=request.user)
blog.delete()
return redirect(reverse('blog_list'))
<a href="{% url 'blog_update' blog.pk %}">수정</a>
{# <a href> 는 get 요청을 보내기에 post 요청을 보내야하는 것과 적합하지 않다 그래서 밑에 처럼쓴다 #}
<form action="{% url 'blog_delete' blog.pk %}" method="POST" style="display: inline">
{% csrf_token %}
<button>삭제</button>
</form>
from django.urls import reverse
from django.shortcuts import get_object_or_404
blog = get_object_or_404(Blog,pk=pk)
from django.urls import include # path 경로에 등록
- path('accounts/', include("django.contrib.auth.urls")),
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
form = UserCreationForm()
form = AuthenticationForm(request, request.POST or None)
from django.shortcut import redirect
# 장고의 설정으로 등록해놓은 것을 상요하려면 , config 의 폴더이름이 바뀌더라도 상관없는것
from django.conf import settings # 이렇게 해주면 세팅이 변해도 상관없다.
return redirect(settings.LOGIN_URL) # LOGIN_URL 쓰려고 임포트 한것
from django.contrib.auth import get_user_model # 장고에 어떤것이 연결되어 있는지 확인한 후 가져오는 것이 이 함수
# 장고에 기본 설정에 있는 user 를 가져오는 것임
User = get_user_model()
from django.contrib.auth.decorators import login_required
# 이걸 등록하면 데코레이터로
@login_required() # 로그인중이 아닐때 생성 하면 로그인 페이지로 돌려보내느 데코레이터 이다, settings.py 에 LOGIN_URL 로 세팅 해놓은 곳으로 보낸다.
from django.core.paginator import Paginator
paginator = Paginator(blogs, 10) # 한페이지당 몇개씩 할지
from django.shortcuts import render , get_object_or_404, redirect
from django.views.decorators.http import require_http_methods
@require_http_methods(['POST']) # 특정 메소드만 넣을수 있는 기능
todo_list = Todo.objects.all().values_list('pk','title')
장고에서 pk 를 객체로 지정해서 넘겨주면 자동으로 그 테이블의 프라이머리 키값을 읽어준다
objects.count() : 몇개있는지
오류확인 : 오류가 없는데 페이지단에 안드면 개발자 도구로 가서 element확인해보고 기타등등 확인
보통 해당 페이지의 상세페이지는 pk 즉 프라이머리 키라고 변수를 선언한다
해당 번호를 지우거나 넘기는 페이지를 입력하면 404 에러를 출력 시켜줘야함
django admin docs 를 검색해서 모르는 admin 기능 확인 가능
return redirect("/gugu/2/") # 앞에 / 가있어야 // 가되서 홈으로 redirect 되고 gugu2가나옴
html 문법
{% for moive in movie_list %}
{% if forloop.counter <= 3 %}
{{ forloop.counter | add : 5 }}
{% endif %}
{% endfor %}
{% for moive in movie_list %}
{% if forloop.counter0 == index %}
{{ forloop.counter | add : 5 }}
{% endif %}
{% endfor %}
{% for i in '12345689'|make_list %}
{% widthration n y i %} -> n/y * i
forloop.counter : for 문 안에서 인덱스가 증가함 설정안하면 1부터
forloop.counter0 : for 문 안에서 인덱스가 증가함 0부터
<h1>
<a href="/book_list/{{num}}/"> Book {{num}} ><!-- 페이지 넘어가는 코드 -->
</h1>
• 'verbose'는 필드의 레이블로 사용됩니다.
• Admin 페이지에서 해당 필드를 보여줄 때(예: created_at 대신)에 레이블로 표시됩니다.
• 즉, 관리자가 보기 좋도록 필드 이름을 한국어로 지정하거나 커스터마이즈할 수 있는 옵션입니다.
• 예를 들어, Django Admin 페이지에서 created_at 필드 대신 '작성일자'가 나타납니다
• auto_now_add=True:
• 레코드가 처음 생성될 때 해당 필드에 자동으로 현재 시간을 저장합니다.
• 이후에는 값을 변경할 수 없고, 레코드 생성 시점의 시간만 유지됩니다.
• 일반적으로 “작성일”이나 “생성일”처럼 한 번 설정된 후 변하지 않는 시간에 사용됩니다.
• auto_now=True:
• 레코드가 저장될 때마다(수정될 때 포함) 해당 필드에 현재 시간을 자동으로 저장합니다.
• 즉, “수정일”처럼 수정될 때마다 최신 시간으로 업데이트해야 하는 필드에 적합합니다.