장고 템플릿을 이용하는 프로젝트 진행 중에 대시보드를 구현해야 했습니다.

대시보드 한 페이지에 다양한 정보가 들어오고 컴포넌트 별로 실시간 업데이트가 필요하지 않을까라는 생각에 ajax로 통신을 하려고 했습니다.

ajax로 통신할 때 url 정보를 보내줘야 하는데 장고 url 태그가 편리하기에 이 장고 코드를 <script> 태그 내에 작성해도 되는지 알아보았습니다.

<script> 태그 내부의 장고 코드

<script> 태그 내부에도 Django 템플릿 태그를 사용할 수 있습니다. 하지만 여기서 주의할 점은 Django 템플릿 태그가 서버 측에서 처리되고, JavaScript 코드는 클라이언트 측에서 실행된다는 것입니다.

예를 들어, Django의 URL을 JavaScript 코드에서 사용하고 싶을 때, 이렇게 할 수 있습니다

<script>
  let listDataUrl = "{% url 'list_data_url' %}";
  let graphDataUrl = "{% url 'graph_data_url' %}";

  // 이후에 이 URL들을 AJAX 요청에서 사용
  // ...
</script>

위의 예제에서, Django 템플릿 태그는 서버 측에서 URL을 생성하고, 이 URL은 JavaScript 변수에 할당됩니다. 이후에 이 변수들을 사용하여 AJAX 요청을 수행할 수 있습니다.

주의할 점

여기서 주의할 점은 Django 템플릿 태그가 서버 측에서 처리되고, JavaScript 코드는 클라이언트 측에서 실행된다는 것입니다.

웹 애플리케이션에서 서버 측과 클라이언트 측은 다른 환경에서 실행되며, 서로 다른 역할을 수행합니다.

서버 측(Server-Side)

  • 서버 측은 웹 서버와 백엔드 애플리케이션 로직이 실행되는 곳입니다.
  • 데이터베이스 쿼리, 인증, 데이터 처리 등과 같은 작업을 처리합니다.
  • Django는 서버 측 웹 프레임워크로, 요청이 들어오면 이에 대한 응답을 생성하는 데 사용됩니다.
  • Django 템플릿 태그는 서버에서 해석되고 처리됩니다. 템플릿 태그는 서버 측에서 HTML 문서를 렌더링 하는데 도움이 됩니다.

클라이언트 측(Client-Side)

  • 클라이언트 측은 사용자의 브라우저에서 실행되는 환경입니다.
  • HTML, CSS, JavaScript로 구성된 웹 페이지를 렌더링 하고, 사용자와 상호작용합니다.
  • JavaScript는 클라이언트 측에서 실행되며, 동적인 기능을 제공합니다 (예: 버튼 클릭, AJAX 요청, 애니메이션 등).

서버 측과 클라이언트 측의 관계

  • 사용자가 웹 페이지를 요청하면, 서버 측(Django)은 HTML 문서를 생성하여 브라우저로 보냅니다.
  • 이 HTML 문서는 Django 템플릿 태그로 작성되었을 수 있으며, 이 태그들은 서버에서 처리되고 최종 HTML로 변환됩니다.
  • 브라우저(클라이언트 측)는 이 HTML 문서를 받아 사용자에게 표시하며, JavaScript는 브라우저에서 실행됩니다.

이렇게 서버 측에서 렌더링 된 결과를 클라이언트 측에서 사용할 수 있습니다. 하지만 JavaScript 코드 자체는 클라이언트에서 실행되기 때문에, 실행 시점에 서버 측에 영향을 미치지는 않습니다.

그래서 script 함수 내부에서는 장고 템플릿 코드를 사용할 수 없습니다.

<script>
$(document).ready(function(){
  function renderList() {
    $.ajax({
      url: "{% url 'list_data_url' %}"; // 이렇게 사용 할 수 없습니다.
      // 생략...
    });
  }
});
</script>

'Python > Django' 카테고리의 다른 글

django에서 *args, **kwargs  (0) 2023.07.25
get_user_model()  (0) 2023.07.22
Django ORM에서 던더(__)  (0) 2023.07.07
Type hint 적용후 circular dependency 문제 해결법  (0) 2023.06.30
FK 관계를 설정하는 방법  (0) 2023.06.30

언더스코어 두 개 (__)는 Django ORM에서 사용되는 문법입니다. 이를 "던더" 또는 "더블 언더스코어"라고도 합니다.

__를 사용하여 필드 이름과 조건을 연결하여 데이터베이스 쿼리를 구성할 수 있습니다. 이를 통해 관련된 모델들 간의 관계를 표현하고, 필터링 또는 관련된 데이터를 가져올 수 있습니다.

예를 들어,
모델의 구조는 아래와 같습니다.

class Department(models.Model):
    company = models.ForeignKey(Company, on_delete=models.CASCADE)
    name = models.CharField(max_length=255)
    is_it = models.BooleanField(default=False)
    explain = models.CharField(max_length=255, blank=True, default="")


class UserProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    department = models.ForeignKey(Department, on_delete=models.SET_NULL, null=True)
    # ... 생략 ...

이 경우 it 부서의 유저 프로필만 가져온다면 아래와 같이 작성할 수 있습니다.

user_profiles = UserProfile.objects.filter(
    department__is_it=True
)

department__is_it=TrueUserProfiledepartment 필드의 is_it 속성을 필터링하는 부분입니다. __을 사용하여 관련된 모델인 Departmentis_it 필드에 접근하고 비교 조건을 설정합니다.

즉, __를 사용하면 관련된 모델 간의 필드를 참조하고, 관련된 데이터를 가져오는 데 사용할 수 있습니다.

'Python > Django' 카테고리의 다른 글

get_user_model()  (0) 2023.07.22
django 템플릿 <script>태그  (0) 2023.07.08
Type hint 적용후 circular dependency 문제 해결법  (0) 2023.06.30
FK 관계를 설정하는 방법  (0) 2023.06.30
(Django) reverse와 reverse_lazy 차이  (0) 2023.06.23

장고(Django)에서 순환 종속성(circular dependency)은 서로가 서로를 의존하는 모듈 또는 패키지 간의 관계를 말합니다. 이는 코드 구조에서 일반적으로 문제가 될 수 있는 상황입니다.

장고 프로젝트는 여러 개의 앱으로 구성될 수 있습니다. 각 앱은 모델, 뷰, 템플릿 등의 구성 요소를 포함하고 있습니다. 종종, 한 앱이 다른 앱의 모델이나 뷰를 참조해야 할 때 순환 종속성 문제가 발생할 수 있습니다.

예를 들어, 앱 A가 앱 B의 모델을 참조하고 앱 B가 앱 A의 모델을 참조한다고 가정해 봅시다. 이 경우, 앱 A를 실행하려면 앱 B의 모델이 필요하고 앱 B를 실행하려면 앱 A의 모델이 필요합니다. 따라서 어느 앱을 먼저 로드해야 할지 결정할 수 없습니다. 이는 순환 종속성이 발생한 상황입니다.

순환 종속성은 일반적으로 장고에서 발생하는 문제 중 하나이며, 이를 해결하기 위해 몇 가지 방법을 사용할 수 있습니다.

  1. 모델 구조 변경: 순환 종속성이 발생하는 경우, 모델 간의 관계를 다시 설계하여 순환 종속성을 제거할 수 있습니다. 모델 간의 관계를 단방향으로 만들거나 중간에 다른 모델을 도입하여 종속성을 해결할 수 있습니다.
  2. 앱 분리: 순환 종속성 문제가 발생한 경우, 관련된 모델, 뷰, 템플릿 등을 다른 앱으로 분리할 수 있습니다. 이렇게 하면 각 앱은 독립적으로 로드될 수 있으며, 순환 종속성 문제가 발생하지 않습니다.
  3. 임시 해결책: 순환 종속성 문제를 일시적으로 해결하기 위해 임시로 lazy_import 등을 사용할 수도 있습니다. 이를 통해 모듈이 필요한 시점에 로드되도록 지연시킬 수 있습니다. 하지만 이 방법은 장기적인 해결책은 아니며, 코드 복잡성을 증가시킬 수 있습니다.

순환 종속성은 코드의 가독성과 유지보수에 문제를 일으킬 수 있으므로, 가능한 한 피하는 것이 좋습니다.

이러한 경우가 아니더라도 타입 힌트를 적용하다보면 순환 종속성 문제를 마주하기 쉽습니다.

예시 코드: company/models.py

from users.models import User, UserProfile

class Department(TimestampZone):
    # ... 생략

    def add_member(self, user: User):
        """부서원으로 user 추가"""
        user_profile: UserProfile = user.user_profile.first()
        user_profile.department = self
        user_profile.save()

company.models 모듈이 users.models를 가져오려고 하고, 동시에 users.modelscompany.models를 가져오려고 합니다. 이러한 상황은 파이썬이 모듈을 불러오는 방식 때문에 문제가 발생합니다.

타입 힌트(type hint)에서만 사용되는 import의 경우, 이 문제를 해결하는 데 좋은 방법은 TYPE_CHECKING 를 사용하는 것입니다. 이를 사용하면 타입 힌트를 위한 import를 실행 시간에 건너뛸 수 있습니다.

예를 들어, 다음과 같이 코드를 수정할 수 있습니다

from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from users.models import User, UserProfile

class Department(TimestampZone):
    # ... 생략

    def add_member(self, user: 'User'):
        """부서원으로 user 추가"""
        user_profile: 'UserProfile' = user.user_profile.first()
        user_profile.department = self
        user_profile.save()

위의 코드에서, TYPE_CHECKING을 사용하여 타입 힌트에 대한 import를 정적 타입 검사기가 실행할 때만 수행하도록 했습니다. 또한 실제 클래스 대신 문자열로 타입 힌트를 제공하여 실행 시간에 해당 이름의 실제 클래스를 찾지 않도록 했습니다. 이러한 방법으로 순환 종속성 문제를 해결할 수 있습니다.

'Python > Django' 카테고리의 다른 글

django 템플릿 <script>태그  (0) 2023.07.08
Django ORM에서 던더(__)  (0) 2023.07.07
FK 관계를 설정하는 방법  (0) 2023.06.30
(Django) reverse와 reverse_lazy 차이  (0) 2023.06.23
Django로 비밀번호 유효성 검사 구현하기  (0) 2023.06.17

장고에서는 Foreign Key (외래 키) 관계를 설정하는 방법으로 크게 세 가지가 있습니다.

1. 일대다 (One-to-Many) 관계

일대다 관계에서는 한 모델이 다른 모델의 여러 인스턴스와 연결될 수 있지만, 두 번째 모델(Book)의 인스턴스는 첫 번째 모델(Author)의 한 인스턴스에만 연결됩니다.

일대다 관계를 설정하기 위해 ForeignKey 필드를 사용할 수 있습니다.

예시:

class Author(models.Model):
   name = models.CharField(max_length=100)

class Book(models.Model):
   author = models.ForeignKey(Author, on_delete=models.CASCADE)
   title = models.CharField(max_length=100)
   publication_date = models.DateField()

위의 예시에서 Book 모델은 Author 모델과 일대다 관계를 갖습니다. 하나의 작가는 여러 개의 책을 작성할 수 있지만, 각각의 책은 하나의 작가에만 속할 수 있습니다.

# views.py
from django.shortcuts import render
from .models import Author

def author_detail(request, author_id):
   author = Author.objects.get(pk=author_id)
   books = author.book_set.all()
   return render(request, 'author_detail.html', {'author': author, 'books': books})
<!-- author_detail.html -->
<h1>{{ author.name }}</h1>
<h2>작품 목록</h2>
<ul>
   {% for book in books %}
       <li>{{ book.title }}</li>
   {% empty %}
       <li>작품이 없습니다.</li>
   {% endfor %}
</ul>

위의 예시에서 Author 모델과 Book 모델은 일대다 관계를 갖습니다. author_detail 뷰에서는 특정 작가의 작품 목록을 가져와서 템플릿에 전달합니다. 템플릿에서는 해당 작가의 작품들을 출력합니다.

2. 일대일 (One-to-One) 관계

일대일 관계에서는 한 모델의 인스턴스가 정확히 다른 모델의 한 인스턴스와 연결됩니다.

일대일 관계를 설정하기 위해 OneToOneField 필드를 사용할 수 있습니다.

예시:

# models.py
class Person(models.Model):
   name = models.CharField(max_length=100)

class Passport(models.Model):
   person = models.OneToOneField(Person, on_delete=models.CASCADE)
   passport_number = models.CharField(max_length=20)
   expiration_date = models.DateField()

위의 예시에서 Passport 모델은 Person 모델과 일대일 관계를 갖습니다. 각 사람은 하나의 여권을 가지며, 각 여권은 하나의 사람에게 속합니다.

# views.py
from django.shortcuts import render
from .models import Person

def person_detail(request, person_id):
   person = Person.objects.get(pk=person_id)
   passport = person.passport
   return render(request, 'person_detail.html', {'person': person, 'passport': passport})
<!-- person_detail.html -->
<h1>{{ person.name }}</h1>
<h2>여권 정보</h2>
<p>여권 번호: {{ passport.passport_number }}</p>
<p>만료일: {{ passport.expiration_date }}</p>

위의 예시에서 Person 모델과 Passport 모델은 일대일 관계를 갖습니다. person_detail 뷰에서는 특정 사람의 여권 정보를 가져와서 템플릿에 전달합니다. 템플릿에서는 해당 사람의 여권 정보를 출력합니다.

3. 다대다 (Many-to-Many) 관계:

다대다 관계에서는 한 모델의 여러 인스턴스가 다른 모델의 여러 인스턴스와 연결될 수 있으며, 그 반대도 가능합니다.

다대다 관계를 설정하기 위해 ManyToManyField 필드를 사용할 수 있습니다.

예시:

# models.py
class Student(models.Model):
   name = models.CharField(max_length=100)

class Course(models.Model):
   name = models.CharField(max_length=100)
   students = models.ManyToManyField(Student)

위의 예시에서 Course 모델은 Student 모델과 다대다 관계를 갖습니다. 각 과목은 여러 학생들이 수강할 수 있고, 각 학생도 여러 과목을 수강할 수 있습니다.

# views.py
from django.shortcuts import render
from .models import Course

def course_detail(request, course_id):
   course = Course.objects.get(pk=course_id)
   students = course.students.all()
   return render(request, 'course_detail.html', {'course': course, 'students': students})
<!-- course_detail.html -->
<h1>{{ course.name }}</h1>
<h2>수강생 목록</h2>
<ul>
   {% for student in students %}
       <li>{{ student.name }}</li>
   {% empty %}
       <li>수강생이 없습니다.</li>
   {% endfor %}
</ul>

위의 예시에서 Course 모델과 Student 모델은 다대다 관계를 갖습니다.

역참조 관계에서도 동일한 원리로 사용할 수 있습니다. 역참조 관계에서는 _set 접미사를 사용하여 역참조 매니저를 사용할 수 있습니다. 예를 들어, 위의 예시에서 Author 모델에는 book_set이라는 역참조 매니저가 자동으로 생성됩니다. 이를 통해 해당 작가와 관련된 모든 책을 가져올 수 있습니다. 마찬가지로 일대일 관계와 다대다 관계에서도 역참조 매니저를 사용할 수 있습니다.

역참조

  1. 일대다 (One-to-Many) 관계에서의 역참조:

    # views.py
    from django.shortcuts import render
    from .models import Book
    
    def book_detail(request, book_id):
        book = Book.objects.get(pk=book_id)
        author = book.author
        return render(request, 'book_detail.html', {'book': book, 'author': author})
    <!-- book_detail.html -->
    <h1>{{ book.title }}</h1>
    <h2>작가 정보</h2>
    <p>작가 이름: {{ author.name }}</p>
    <p>작가 이메일: {{ author.email }}</p>

    위의 예시에서 Book 모델은 Author 모델과 일대다 관계를 갖습니다. book_detail 뷰에서는 특정 책의 작가 정보를 가져와서 템플릿에 전달합니다. 템플릿에서는 해당 책의 작가 정보를 출력합니다.

  2. 일대일 (One-to-One) 관계에서의 역참조:

    # views.py
    from django.shortcuts import render
    from .models import Passport
    
    def passport_detail(request, passport_id):
        passport = Passport.objects.get(pk=passport_id)
        person = passport.person
        return render(request, 'passport_detail.html', {'passport': passport, 'person': person})
    <!-- passport_detail.html -->
    <h1>여권 정보</h1>
    <p>여권 번호: {{ passport.passport_number }}</p>
    <p>만료일: {{ passport.expiration_date }}</p>
    <h2>소유자 정보</h2>
    <p>이름: {{ person.name }}</p>
    <p>나이: {{ person.age }}</p>

    위의 예시에서 Passport 모델은 Person 모델과 일대일 관계를 갖습니다. passport_detail 뷰에서는 특정 여권의 소유자 정보를 가져와서 템플릿에 전달합니다. 템플릿에서는 해당 여권의 소유자 정보를 출력합니다.

  3. 다대다 (Many-to-Many) 관계에서의 역참조:

    # views.py
    from django.shortcuts import render
    from .models import Student
    
    def student_detail(request, student_id):
        student = Student.objects.get(pk=student_id)
        courses = student.course_set.all()
        return render(request, 'student_detail.html', {'student': student, 'courses': courses})
    <!-- student_detail.html -->
    <h1>{{ student.name }}</h1>
    <h2>수강한 과목 목록</h2>
    <ul>
        {% for course in courses %}
            <li>{{ course.name }}</li>
        {% empty %}
            <li>수강한 과목이 없습니다.</li>
        {% endfor %}
    </ul>

    위의 예시에서

    Student 모델과 Course 모델은 다대다 관계를 갖습니다. student_detail 뷰에서는 특정 학생이 수강한 과목 목록을 가져와서 템플릿에 전달합니다. 템플릿에서는 해당 학생이 수강한 과목들을 출력합니다.

위의 예시 코드에서는 역참조 매니저인 _set을 사용하여 역참조를 수행하고 있습니다. 역참조 매니저를 사용하면 해당 모델과 관련된 역방향 관계에 있는 객체들을 가져올 수 있습니다.

related_name

related_name은 역참조 시 사용되는 이름을 지정하는 기능입니다. 다음은 related_name을 사용하는 예시입니다.

class Author(models.Model):
    name = models.CharField(max_length=100)

class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books')

위의 예시에서 Book 모델의 author 필드는 Author 모델과의 외래키 관계를 나타냅니다. related_name='books'Author 모델에서 Book 모델과의 역참조 시 사용할 이름을 지정하는 부분입니다.

이제 역참조를 사용하여 Author 모델과 관련된 책들을 가져오는 예시를 보겠습니다.

author = Author.objects.get(id=1)
books = author.books.all()

위의 코드에서 author.books.all()Author 모델의 역참조 매니저 books를 통해 해당 작가와 관련된 모든 책들을 가져옵니다.

related_name을 설정함으로써 역참조 시 사용할 이름을 명시적으로 지정할 수 있으며, 이를 통해 보다 직관적이고 명확한 코드를 작성할 수 있습니다.

reverse와 reverse_lazy 차이

  1. reverse
  • reverse 메소드는 주어진 뷰에 대한 URL을 생성하는데 사용됩니다. 이 메서드는 URL 패턴의 이름과 뷰에 전달해야하는 인수를 받고 URL을 나타내는 문자열을 반환합니다.
  • 이 함수는 호출되는 즉시 평가(evaluated)됩니다. 즉, URL을 즉시 생성합니다.
  • reverse는 즉시 평가되기 때문에 아직 로드되지 않을 수 있는 URL 구성(configurations)(예: Django의 URL 구성에 의존하는 클래스 기반 뷰의 속성을 정의할 때)에는 사용해서는 안됩니다.
  1. reverse_lazy
  • 반면에 reverse_lazy는 reverse와 동일한 작업을 수행하지만 더 느립니다. 즉, 명시적으로 액세스(explicitly accessed)할 때까지 URL을 생성하지 않습니다.
  • 이는 success_url과 같은 클래스 기반 뷰와 같이 URLConf가 완전히 로드되지 않은 상태에서 URL을 사용해야할 때 유용합니다.

reverse_lazy는 일반적으로 클래스 기반 뷰에서 success_url을 정의할때 사용됩니다. success_url 속성은 클래스 수준(level)에서 값이 필요하지만, 이 시점에서 모든 URL 패턴이 로드되지 않을 수 있습니다. reverse_lazy를 사용하면 모든 패턴이 로드되고 실제로 URL이 필요할 때까지 URL을 연기합니다.

반면에 reverse는 LoginView의 post 메서드나 log_out 함수와 같이 메서드(함수) 내부에서 사용됩니다. 이러한 경우 메서드가 호출될때쯤이면 모든 URL 구성(configurations)이 로드될 것이므로 리버스 방식을 사용하는 것이 안전하다고 합니다.

쉬운 설명

이걸 쉬운 예시로 들면, reverse 메서드는 마법의 지도에게 지금 당장 보물로 가는 길을 알려달라고 하는 것과 같습니다. 그래서 그 마법지도는 즉시 어디로 가야하는지 알려줄 것입니다.

하지만 마법 지도가 아직 길을 그리고 있느라 준비되지 않는 경우도 있습니다. 이 경우 너무 일찍 길을 물어보면 마법 지도가 올바른 답을 줄 수 없을 것입니다. 이때 사용하는게 reverse_lazy입니다.

reverse_lazy 메서드는 마법 지도에게 "마법지도가 완성되면 보물로 가는 길을 알려줘!"라고 말하는 것과 같습니다. 그래서 마법 지도는 모든 경로를 그리고, 준비가 되면 보물이 있는 위치를 알려줍니다.

결국 마법 지도가 준비되었다는 것을 알면 reverse를 사용하고, 마법 지도가 아직 그리고 있을때는 reverse_lazy를 사용합니다.

예시 코드

class LoginView(View):
    """
    Handles user login.
    """
    template_name = "users/login.html"

    def get(self, request):
        form = LoginForm()
        return render(request, self.template_name, {"form": form})

    def post(self, request):
        form = LoginForm(request.POST)

        if form.is_valid():
            email = form.cleaned_data.get("email")
            password = form.cleaned_data.get("password")
            user = authenticate(request, email=email, password=password)

            if user:
                login(request, user)
                return redirect(reverse("users:logged_in_index"))

        return render(request, self.template_name, {"form": form})


@login_required
def log_out(request):
    """
    Logs out the user and redirects them to the index page.
    """
    logout(request)
    return redirect(reverse("users:index"))

요구 정책

  • 8자리 이상
  • 영문, 숫자, 특수문자 포함

구현 방식

  • DRF가 아닌 Pure Django에서 구현했습니다.
  • validators.py에 원하는 검사를 구현하고, forms.py에 validator를 추가했습니다.

구현

settings.py

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
    {
        'NAME': 'users.validators.CustomPasswordValidator', # 추가
    },
]

VALIDATORS를 추가하였습다. 구현한 validators.py에서도 기존의 validator도 사용하기 때문에 기존 validator 및에 추가하였습니다.

validators.py

from django.core.exceptions import ValidationError
from django.contrib.auth.password_validation import validate_password
import re


class CustomPasswordValidator:
    def __call__(self, value): # 1)
        try:
            validate_password(value)
        except ValidationError as e:
            raise ValidationError(str(e))

    def validate(self, password, user=None):
        """비밀번호 유효성 검사"""

        if len(password) < 8:
            raise ValidationError("비밀번호는 8자리 이상이어야 합니다.")
        if not re.search(r"[a-zA-Z]", password):
            raise ValidationError("비밀번호는 하나 이상의 영문이 포함되어야 합니다.")
        if not re.search(r"\d", password):
            raise ValidationError("비밀번호는 하나 이상의 숫자가 포함되어야 합니다.")
        if not re.search(r"[!@#$%^&*()]", password):
            raise ValidationError(
                "비밀번호는 적어도 하나 이상의 특수문자(!@#$%^&*())가 포함되어야 합니다."
            )

    def get_help_text(self): # 2)
        return "비밀번호는 8자리 이상이며 영문, 숫자, 특수문자((!@#$%^&*()))를 포함해야 합니다."

이렇게 구현했습니다.
위에처럼 정규 표현식이 아닌 아래처럼 일반 파이썬 문으로도 구현이 가능합니다.

        if not any(char.isalpha() for char in value):
            raise forms.ValidationError("비밀번호는 하나 이상의 영문이 포함되어야 합니다.")
        if not any(char.isalpha() for char in value):
            raise forms.ValidationError("비밀번호는 하나 이상의 숫자가 포함되어야 합니다.")
        if not any(char in "!@#$%^&*()" for char in value):
            raise forms.ValidationError("비밀번호는 적어도 하나 이상의 특수문자((!@#$%^&*()))가 포함되어야 합니다.")

1)

이 클래스 CustomPasswordValidator는 callable 객체를 정의합니다. 클래스의 인스턴스를 함수로 호출할 수 있는 __call__ 메서드를 구현합니다. 이 경우 'CustomPasswordValidator' 인스턴스는 암호 값을 인수로 사용하여 호출할 수 있습니다.

__call__ 메서드 내에서 validate_password 함수가 호출되어 django가 기본으로 제공하는 암호 유효성 검사를 실행합니다. 유효성 검사 오류가 발생하면 오류 메시지와 함께 ValidationError가 발생합니다.

다시 말하면, __call__이 메소드는 SignUpForm 클래스의 validators=[CustomPasswordValidator()] 행과 같이 클래스의 인스턴스가 함수로 호출될 때 호출됩니다. CustomPasswordValidator()가 호출되면 __call__ 메서드가 트리거되고 validate_password() 함수를 호출합니다.

또한 CustomPasswordValidator 클래스는 validate 메서드도 정의합니다. 이 메서드는 인스턴스가 함수로 호출될 때(__call__ 메서드와 같이) 자동으로 실행되지 않지만 validate_password()가 수행하는 것 이상의 추가 암호 유효성 검사 규칙을 제공합니다. 이러한 추가 규칙은 비밀번호의 길이와 영문, 숫자 및 특수 문자를 포함하는지 여부를 확인합니다.

따라서 SignUpForm 클래스에서 CustomPasswordValidator()를 유효성 검사기로 사용하면 validate_password() 함수와 validate() 메서드가 모두 실행되어 비밀번호를 검증합니다.

2)

get_help_text 메서드는 암호 요구 사항에 대한 정보를 제공하는 도움말 텍스트를 반환합니다. 이는 예상되는 암호 형식에 대한 추가 정보를 사용자에게 표시하는 데 유용할 수 있습니다.

forms.py

from django import forms
from .validators import CustomPasswordValidator


class SignUpForm(forms.Form):
    ... 생략 ...
    password = forms.CharField(
        widget=forms.PasswordInput,
        validators=[CustomPasswordValidator()],
    )
    ... 생략 ...

이렇게 import 해서 사용합니다.

UniqueConstraint에 대한 설명과 django Model클래스의 Meta 클래스에 대한 설명은 아래의 글을 통해 확인할 수 있습니다.

 

Django Model에서의 Meta 클래스

Meta Python에서 'Meta'클래스는 일반적으로 Django 프레임워크에서 models, forms, serializers, views 와 같은 클래스에 대한 추가 정보를 제공하는 데 사용합니다. Meta 클래스는 Django 클래스에 대한 다양한 옵

yesaroun.tistory.com

 

UniqueConstraint 사용 예시

1. True이면서 고유한 경우만

class Employee(models.Model):
    name = models.CharField(
        max_length=100,
    )
    department = models.ForeignKey(
        Department,
        on_delete=models.CASCADE,
    )
    is_manager = models.BooleanField(
        default=False,
    )

    class Meta:
        constraints = [
            models.UniqueConstraint(
                name="unique_manager",
                fields=["department", "is_manager"],
                condition=models.Q(is_manager=True),
            ),
        ]

이 예시는 Employee 모델이 있고 각 department에 고유한 관리자(manager)가 있는지 확인할 수 있는 예시입니다. 관리자인 경우(is_manager가 True)이면서, 고유한 department인지를 UniqueConstraint를 정의해서 확인할 수 있습니다.

2. null이면서 고유한 경우만

class MyModel(models.Model):
    field1 = models.CharField(null=True)
    field2 = models.CharField(null=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                name="unique_fields",
                fields=["field1", "field2"],
                condition=models.Q(field1__isnull=False) & models.Q(field2__isnull=False),
            ),
        ]

기존 'unique_together'를 사용하면 null 값을 포함하는 필드 간에 고유성을 적용할 수 없었습니다. 하지만 'UniqueConstraint'에서는 null 값을 다룰 수 있습니다. 위의 예시는 'field1', 'field2' 둘 다 null이 아닌 값에만 적용되도록 하는 조건이 있습니다.

 


부족하거나 잘못된 내용이 있을 경우 댓글 달아주시면 감사하겠습니다.

이 글에 부족한 부분이 존재할 경우 추후에 수정될 수 있습니다.


Meta

Python에서 'Meta'클래스는 일반적으로 Django 프레임워크에서 models, forms, serializers, views 와 같은 클래스에 대한 추가 정보를 제공하는 데 사용합니다.
Meta 클래스는 Django 클래스에 대한 다양한 옵션과 구성을 지정하는 방법입니다.

Model에서의 Meta 클래스

Model 클래스에서 Meta 클래스는 다양한 방법으로 모델의 동작을 지정하는데 사용됩니다.
Model 클래스에서 Meta 클래스에서 주로 사용되는 옵션은 아래와 같습니다.

  • 'db_table' : 모델에 사용할 DB 테이블 이름을 지정합니다. 지정하지 않으면 Django는 앱 이름과 모델 이름을 기반으로 테이블 이름을 자동으로 생성합니다.
  • 'ordering' : QuerySet에 대한 기본 순서를 지정합니다. 예를 들어 아래의 코드는 'created_at' 필드를 기준으로 QuerySet를 내림차순으로 정렬합니다.
    ordering = ['-created_at']
  • 'unique_together' : 특정 필드 조합에 unique 제약 조건을 지정합니다. 예를 들어 아래의 코드처럼 'first_name'과 'last_name'가 모두 같지 않도록 unique 제약 조건을 지정할 수 있습니다. 즉, 두 명의 사용자가 동일한 '성과 이름 조합'을 가질 수 없도록 합니다. 다만, Django에서는 이 'unique_together' 방식을 제거하고 아래서 설명할 'UniqueConstraint' 방식을 권장합니다.
    unique_together = [
      ['first_name'],
      ['last_name'],
    ]
  • 'UniqueConstraint' : Django 3.2부터 도입된 unique_together 옵션보다 더 유연한 방식의 메소드입니다. 'UniqueConstraint'에 관련한 글은 아래에 첨부하였습니다.
    class Meta:
      constraints = [
          models.UniqueConstraint(
              fields=[
                  'first_name',
                  'last_name',
              ], 
              name = 'unique_fields',
          ),
      ]
     
 

UniqueConstraint 사용 예시

UniqueConstraint에 대한 설명과 django Model클래스의 Meta 클래스에 대한 설명은 아래의 글을 통해 확인할 수 있습니다. https://yesaroun.tistory.com/entry/Django-Model에서의-Meta-클래스 UniqueConstraint 사용 예시 1. T

yesaroun.tistory.com

 

 


부족하거나 잘못된 내용이 있을 경우 댓글 달아주시면 감사하겠습니다.

이 글에 부족한 부분이 존재할 경우 추후에 수정될 수 있습니다.


 

Django의 admin 패널에서 모델을 관리하기 위해서는 해당 모델을 등록해야 한다. 등록하는 방식에는 두 가지가 있다.

1. admin.site.register()

from django.contrib import admin
from .models import User

admin.site.register(User)

또는

from django.contrib import admin
from .models import User


class UserAdmin(admin.ModelAdmin):
    pass

admin.site.register(User, UserAdmin)

2. 데코레이터 함수

from django.contrib import admin
from .models import User


@admin.register(User)
class UserAdmin(admin.ModelAdmin):
    pass

이 두 가지 방식의 차이점은 가독성과 코드 구성이다. 개인적으로는 데코레이터 함수를 사용하는 방식이 더 가독성이 좋다고 생각한다. 데코레이터 함수를 사용하면 함수의 역할이 명확해지고, 코드의 구성이 보다 명확해진다. 하지만, admin.site.register()의 경우 매우 간단하게 admin 등록을 할 필요가 있을 때 사용하면 간편하게 등록이 가능하다는 장점이 있다. 따라서 각 상황에 맞게 적절한 방식을 선택해야 한다.

 

 

 


부족하거나 잘못된 내용이 있을 경우 댓글 달아주시면 감사하겠습니다.

이 글에 부족한 부분이 존재할 경우 추후에 수정될 수 있습니다.