Study/django

devspoon-portfolio-blog 오픈소스에 admin 기능 구현하기

bluebamus 2022. 12. 11.

* 참고 : https://docs.djangoproject.com/en/4.1/ref/contrib/admin/

 

Django

The web framework for perfectionists with deadlines.

docs.djangoproject.com

 

1. app별 admin 분리 (완료)

 - app별로 site_header, site_title, index_title 적용

 - user에 get_app_list으로 메뉴 출력 순서 소팅

2. 리스트 페이지 수정 

 - action 적용 

    - is_deleted 불리언 제어 : delete/activate

    - is_hidden 불리언 제어 : hidden/visible

    - 상위 기능 mixin으로 제공

    - user는 별개로 직접 구현

 - 커스텀 필드 적용 : 상호 작용/ 검증할 내용 없음 적용 안함

 - SimpleListFilter 적용

    - user에만 적용

 - rangefilter 설치 : 날자, 시간별로 필터 가능

     - 설치 명령어 : pip install django-admin-rangefilter

 - get_readonly_fields : 썸네일 출력

     - 썸네일이 없는 경우 공백으로 처리

     - readonly_fields에 해당 필드를 등록을 해 놓고, get_readonly_fields로 해당 동작이 실행전 검사를 통해 출력 여부 설정

         - 예) obj가 있다면 해당 필드를 그대로 유지, 없다면 제거

    - 다른 필드에 있어도 get_***_fields로 사전 처리가 가능할 것으로 예상됨

 - 커스텀 버튼 달기 : 당장 추가해야 할 기능이 없어서 삭제 / html 오버라이딩 기능이기에 다른 구현 사항 참고

 - boolean 아이콘 : 특정 값을 불리언으로 만들면 django amin에서 자동으로 아이콘 생성함

 - editable : 해당 동작은 model에서 설정가능하다.

     - 예를 들어 아래 코드와 같이 모델에 선언하면, 해당 코드는 admin에서 보이지 않는다. 

class Article(models.Model):
    title      = models.CharField('제목', max_length=126, null=False)
    content    = models.TextField('내용', null=False)
    author     = models.CharField('작성자', max_length=16, null=False)
    created_at = models.DateTimeField('작성일', auto_now_add=True)
    # created의 editable 속성에 True를 설정
    created_at.editable = True

     - 하지만, admin 자체에서 exclude = ['created_at',] 이렇게 선언해도 동일하게 동작된다.

-  response_change ***

     - response_change는 관리 양식이 제출된 후 객체 및 모든 관련 인스턴스가 저장된 직후에 호출됩니다. 

     - 객체가 변경된 후 이를 재정의하여 기본 동작을 변경할 수 있습니다.

     - 예를 들어 아래와 같이 오버라이딩된 htrml 코드가 있다면, name에 선언된 값을 참고로 if를 통해 분기하여

       원하는 작업을 추가적으로 실행하고 redirect할 수 있다.

<div class="submit-row">
        <input type="submit" value="Make Unique" name="_make-unique">
</div>

 - list_editable : 인스턴스로 이동하지 않고 리스트 페이지에서 바로 수정을 할 수 있도록 지원한다.

 - search_fields : 검색에 반영할 필드 지정 

 - autocomplete_fields : search_fields와 연계하여 설정할 수 있음 - 조건이 까다로워서 적용 안함

 - admin_order_field : 정렬기능을 추가할 수 있음 - 적용 안함

 - short_description : 리스트의 컬럼 항목의 제목을 수정할 수 있음

 - boolean : True, False 대신 O, X의 아이콘 모양으로 출력함

 

3. 디테일 페이지 수정

 - form 적용

 - save_model : 특정 페이지 저장시 자동으로  특정 필드 업데이트

 - inlines : 다른 모델의 내용을 한 페이지에서 저장, 수정을 할 수 있도록 페이지를 결합해 준다

     - StackedInline, TabularInline 두 가지중 하나를 선택할 수 있으며, 가로 세로의 결합 화면을 만들어 준다.

class UserProfileInline(admin.TabularInline):
    model = UserProfile # OneT

class CustomUserAdmin(UserAdmin):
    ....
    inlines = [UserProfileInline]

 - readonly_fields : 수정 없이 읽기만 가능한 필드 

 - raw_id_fields : 대량의 데이터가 있는 경우 select로 찾기가 어렵다. 새로운 창을 띄워 전체를 쉽게 확인할 수 있도록 지원하며 페이지네이션 또한 자동으로 지원한다.

 - formfield_for_foreignkey : 외래키 참조로 셀렉트 박스의 내용을 재정의 할 수 있다. 

      - 세가지 경우에 대한 예시 코드를 정리함

      1) 셀렉트 선택 항목 제한

def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "category":
            #kwargs["queryset"] = Category.objects.filter(name__in=['a', 'b'])

      2) 선택하는 목록들의 출력 내용 재 정의

class CategoryChoiceField(forms.ModelChoiceField):
     def label_from_instance(self, obj):
         return "Category: {}".format(obj.name)
         
         
@admin.register(Hero)
class HeroAdmin(admin.ModelAdmin, ExportCsvMixin):
    ....
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "category":
            return CategoryChoiceField(queryset=Category.objects.all())
        return super().formfield_for_foreignkey(db_field, request, **kwargs)

      3) 외래키 참조에 obj를 직접 접근해서 queryset을 가져와야 하는 경우

# reference : https://wikidocs.net/10437
# formfield_for_foreignkey 메소드에서 request.user 객체는 바로 접근할 수 있지만 
# 참조하는 인스턴스에는 바로 접근할 수 없다
# get_form() 메소드를 오버라이딩해서 request 객체에 현재 인스턴스를 담아두고 
# formfield_for_foreignkey 필드에서 꺼내 쓰는 형식이다.

def get_form(self, request, obj=None, **kwargs):
    request.current_object = obj
    return super(MessageAdmin, self).get_form(request, obj, **kwargs)

def formfield_for_foreignkey(self, db_field, request, **kwargs):
    instance = request.current_object

    if db_field.name == 'category':
        kwargs['queryset'] = Category.objects.filter(board=instance.board)
    return super(MessageAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

 

 - fieldsets : 상세 페이지를 관련된 항목끼리 그룹화 하여 분류, 출력할 수 있다 아래 코드 참고

    fieldsets = [
        ('key information', {'fields': ['username', 'email', 'password', 'nickname','gender']}),
        ('detail information', {'fields': ['date_joined', 'last_login_at', 'last_login','verified', 'is_superuser', 'is_staff', 'is_active','is_privacy_policy', 'is_terms_of_service', 'is_mobile_authentication', 'is_deleted']}),
        ('more information', {'fields': ['profile_image', 'deleted_at']}),

     - 해당 코드는 3개의 그룹으로 분류된다. 처음 오는 문자열은 그룹 제목이 된다.

- 보기/감추기 (접기 기능)

     - fieldsets에 'classes' : ['collapse']를 속성으로 추가

     - inlines의 경우는 테스트 안해봄

fieldsets = [
        ('key information', {'fields': ['username', 'email', 'password', 'nickname','gender'],'classes': ['collapse']}),
        ('detail information', {'fields': ['date_joined', 'last_login_at', 'last_login','verified', 'is_superuser', 'is_staff', 'is_active','is_privacy_policy', 'is_terms_of_service', 'is_mobile_authentication', 'is_deleted'],'classes': ['collapse']}),
        ('more information', {'fields': ['profile_image', 'deleted_at'],'classes': ['collapse']}),
    ]

4. csv 적용

 - 내보내기

 - 가져오기

5. 그래프 : 구글 애널리틱스 적용 후 그래프 구현할 예정

- 참고 : 

     - https://velog.io/@codren/Admin-%EB%A9%94%EC%9D%B8-%ED%8E%98%EC%9D%B4%EC%A7%80-%EC%BB%A4%EC%8A%A4%ED%84%B0%EB%A7%88%EC%9D%B4%EC%A7%95

 

Admin 메인 페이지 커스터마이징 1 (그래프 출력)

Admin 메인 페이지 커스터마이징, Chart.js를 이용한 그래프 출력

velog.io

     -  https://yomangstartup.tistory.com/42?category=963415 

 

Django Admin 페이지 그래프 만들기 graph - 검색 기록 데이터 저장 및 그래프

해야 하는 것 사용자가 검색을 한 데이터를 저장을 하여 나중에 분석을 하기 위하여 사용하려 한다. 현재까지 구글 파이어 베이스를 사용하였지만.. 부족한 느낌이다. 그래서 따로 데이터 베이

yomangstartup.tistory.com

 

 

댓글