1)
path('admin/', admin.site.urls),
+
admin.site.register(Item)
admin 모듈의 AdminSite 클래스의 urls 메소드를 사용하는 명령어이다. django/contrib/admin/sites.py에서 확인 가능하다. 먼저 주어진 모델이 정의된 admin 클래스를 하에서 등록되는데 이 때 정의된 admin 클래스가 없다면 ModelAdmin을 기본으로 사용한다. 앞서 gallery 앱에서 정의한 Item 모델을 admin.py에서 등록해줬다.
propery 장식자가 걸려 있는 urls 메소드 부분은 get_urls()를 반환하는데 이 부분이 주어진 모델을 활용한 admin 인터페이스를 보여주는 Django의 view에 접근할 수 있도록 해준다. (wrapper 함수는 더 공부해야겠지만,) self.admin_view를 반환하는 것을 확인할 수 있다.
"An AdminSite object encapsulates an instance of the Django admin application, ready to be hooked in to your URLconf. Models are registered with the AdminSite using the register() method, and the get_urls() method can then be used to access Django view functions that present a full admin interface for the collection of registered models."
2)
@admin.register(Item)
class ItemAdmin(admin.ModelAdmin):
list_display = ['id']
리스트의 column을 관리하려고 admin.register를 장식자를 이용해서 등록해주었다.
models.py에서 기본 문자열 __str__을 지정해주더라도 모델을 어드민화하는 list_display 작업이 우선권을 갖는다. list_display는 BaseModelAdmin을 상속하는 ModelAdmin 아래 옵션으로 지정돼있다. 이는 django/contrib/admin/options.py에서 확인 가능하다.
def subject_length(self): return len(self.subject) subject_length.short_description = "suject 글자 수"
해당 리스트에는 속성뿐만 아니라 정의된 함수 'subject_length'도 사용될 수 있다.
subject_length 부분은 models.py가 아닌 admin.py에서도 구현해줄 수 있는데 이때는 Item 클래스가 item 인스턴스로서 전달되는 부분을 추가해준다.
모델 리스트에서 가장 먼저 오는 하나의 속성에 링크가 잡히게 되는데
list_display_links
를 활용하면 이를 별도로 관리할 수 있다.
3)
@admin.register(Item)
class ItemAdmin(admin.ModelAdmin):
...
search_fields = ['subject']
list_filter = ['created_at']
search_fields를 설정하면 흔히 검색창이라 불리는 것을 만들 수 있다. 이는 python manage.py shell에서도 수행한 작업인데, 내가 원하는 검색어에 대한 query string을 만들고 이를 db에 query하는 과정을 간결하게 만들어 준 것이다.
list_filter는 이를 좀 더 모듈화한 것으로 이해된다.
4)
manage.py가 있는 경로에 requirements.txt 파일을 만들고 설치한 라이브러리를 정리한다. 버전별 기능 차이가 있는 것은 버전을 명시한다. 나중에 pip install -r requirements.txt 명령어로 한 번에 설치가 가능하다.
5)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
settings.py가 있는 경로를 절대화해서 그 부모의 부모 폴더인 프로젝트 폴더가 BASE_DIR로 지정된다.
해당 ROOT 폴더에 media 파일이 쌓이게 되면 혼선이 발생할 수 있으므로 media 폴더를 만들어서 저장한다. 이때 '..' 등의 명령어로 상위에 폴더를 생성할 수 있다.
6)
from django.conf.urls.static import static
from django.conf import settings
...
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
MEDIA_ROOT은 저장 시 사용되고(이때 upload_to가 하위 경로를 결정한다. 그렇지 않다면 media 폴더에 너무 많은 파일이 생성될 것이다.) MEDIA_URL은 접근 시 사용되지만, 아직 읽는 방법은 확립되지 않았다.
static 함수는 debug 모드에서 읽으려는 파일을 서빙하기 위한 url 주소를 반환한다. view=serve 부분이 핵심 기능으로 보인다. 이 때 import하는 settings는 conf의 global_settings 위에 프로젝트의 settings를 덮어쓴 상태이다.
7)
def photo_tag(self, item):
if item.photo:
return mark_safe(f'<img src="{item.photo.url}" />')
return None
photo는 blank=True 상태이므로 item의 photo가 있는 경우만 추려서 list_display에 넣어줄 것이다. FileField는 Storage 클래스를 상속한다고 했는데 해당 클래스의 url 메소드가 url 주소를 전달한다.
django 보안 상 이미지가 바로 표시되지 않으므로 mark_safe를 씌워준다. SafeString 클래스를 통해서 SafeData 클래스의 __html__ 메소드를 호출하는데, 간단히 말해서 django에서 안전한 데이터를 다른 템플릿 엔전에서도 안전하도록 html화하는 작업이라고 한다.