ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Day-2(Signal, get_object & get_queryset, form)
    TIL & Todo List/Coding for Entrepreneures 2020. 1. 13. 00:25

    Signal(pre_save & post_save)을 이용한 SlugField

    • django의 Signal은 특정 메서드나 함수가 실행될 때 일어나는 신호(signal)를 받아서 추가 작업을 진행할 수 있도록 한다.
      • 예) 모델이 저장될 시점의 전(pre_save), 후(post_save)에 추가적인 작업을 구현할 수 있다.
    • Signal은 signal을 정의한 모델을 사용하는 모든 앱에서 동작한다.
      • videos에 정의된 모델(signal을 포함하는)을 다른 앱에서 저장하거나 업데이트를 할 때 signal이 동작한다.
    • pre_save, post_save 외에 model signal, management signal, request/response signal 등 다양한 signal이 있다.

     

    SlugField는 url에서 look up field로 사용될 수 있으며, 웹 검색 시 SEO(Search Engine Optimization)을 향상하는데 도움을 준다.

    # modles.py
    
    # pre_save()
    def pre_save_video_receiver(sender, instance, *args, **kwargs):
        instance.slug = slugify(instance.title)
    
    pre_save.connect(pre_save_video_receiver, sender=Video)
    
    
    # post_save()
    def post_save_video_receiver(sender, instance, *args, **kwargs):
        instance.slug = slugify(instance.title)
        instance.save()  # 무한 재귀가 일어남
        
    post_save.connect(post_save_video_receiver, sender=Video)    
    
    
    # decorator를 이용한 pre_save()
    @receiver(pre_save, sender=Video)
    def pre_save_video_receiver(sender, instance, *args, **kwargs):
        instance.slug = slugify(instance.title)
    

    - SlugField를 자동으로 채우기 위해 signal을 이용할 수 있다.

    - slugify(django.utils.text.slugify): 입력된 매개변수를 slug 형태로 변환시켜주는 메서드

    • pre_save()는 slug를 생성하고 이를 instance.slug에 할당하고 이후에 save()
    • post_save()는 instance.save()가 일어난 후 slug를 생성하고 instance.slug에 할당하기 때문에 database에 slug가 저장되지 않는다.
      • instance.save()를 post_save안에 등록할 경우 save 호출 -> post_save호출 -> save호출.... 무한 재귀가 일어난다.
    • 따라서 instance의 필드에 추가로 변경된 값을 저장해야 할 경우 pre_save()를 이용해야 한다.

     

    Overriding get_object

    get_object(): 응답에 필요한 객체를 가져오기 위해 사용되는 메서드

    # views.py
    class VideoDetailView(DetailView):
        queryset = Video.objects.all()
    
        # get_object를 오버라이딩 하지 않을 경우 자동으로 url에서 id 또는 slug를 검색하고 해당 객체를 반환한다.
        def get_object(self, queryset=None):
            slug = self.kwargs.get('slug')
            return get_object_or_404(Video, slug=self.kwargs.get(slug))
            
    # urls.py
    urlpatterns =[
    	...
    	path('videos/<slug>', VideoDetailView.as_view(), name='video-detail'),
    ]
    • self.kwargs.get('slug')는 urls.py에 등록된 path의 slug를 의미한다.
      • self.kwargs -> django.views.generics.base.View

     

    get_queryset(): 응답에 필요한 쿼리셋을 가져오기 위해 사용되는 메서드

    class VideoListView(ListView):
        # queryset = Video.objects.all()  # -> Video의 모든 객체
    
        def get_queryset(self):
        	# Video에서 title에 'django'를 포함하는 모든 객체
            return Video.objects.filter(title__icontains='django')
    
    • get_queryset()을 오버 라이딩하여 사용자가 원하는 쿼리 셋을 가져오기 위한 동작을 구성할 수 있다.

     

    Form(공식 문서) - CreateView, UpdateView, DeleteView

    • ListView와 DetailView는 클라이언트가 요청한 객체를 전달하기 위해 queryset 또는 get_object를 이용한다. 이와 달리 Create, Update, Delete 작업을 위한 클래스에서는 queryset이 아닌 model 속성을 등록해서 사용한다.
    • POST를 사용할 때 csrf_token(공식 문서) 태그를 반드시 사용해야 한다.
      • Cross Site Request Forgery(CSRF): 사용자가 의도한 요청이 아닌, 외부 유저(해커)가 사이트에 요청을 대신 전달하게 하는 공격 방식
      • csrf_token은 페이지에 접속할 때 유저에게 token을 전달하고 post 동작이 필요할 때 django가 유저의 token을 검사하는 방식을 사용한다.
        1. 게시글을 작성하기 위한 페이지에 접속(유저는 token_a 획득)
        2. 글 작성을 위한 POST 실행
        3. django에서 토큰 확인(token_a? token_b?)
          (중간 과정에서 해커가 접속하여 다른 동작(공격)을 시도하더라도 토큰(token_a)을 가지고 있지 않기 때문에 공격 불가)
    # views.py
    class VideoCreateView(CreateView):
        # queryset = Video.objects.all()  # ImproperlyConfigured 에러를 일으킨다.
        model = Video
        form_class = VideoForm
        
    # forms.py
    class VideoForm(forms.ModelForm):
        class Meta:
            model = Video
            fields = [
                'title', 'embed_code'
            ]
    
    # templates/video_create.html(content 블록)
    {% block content %}
    
    <h1>Hello!!</h1>
    <form method="POST" action="" >
        {% csrf_token %}
        {{ form.as_p }}
        <input type="submit" value="Save" class="btn btn-primary">
    </form>
    {% endblock %}

    form의 field에 등록된 두 개의 필드(title, embed code)에 대한 form이 생성된다.
    폼에서 저장한 페이지 출력

     

    Search view

    get 메서드를 이용한 검색 기능 구현

    # views.py
    class VideoListView(ListView):
        def get_queryset(self):
            q = self.request.GET.get('q')
            qs = Video.objects.all()
            if q:  # query string이 있을 경우
                qs = qs.filter(title__icontains=q)
            return qs
    
    
    # templates/video_list.py
    ...
    <form method="GET" action="{% url 'video-list' %}">
        <input type="text" placeholder="Search.." name="q">
    </form>
    {{ object_list }}
    
    <ul>
        {% for item in object_list%}
        <li>
            <a href="{{ item.get_absolute_url }}">
                {{ item.title}}
            </a></li>
        {% empty %}
        <li>No item Found</li>
        {% endfor %}
    </ul>

    self.request.GET.get('q'): 요청(get)한 url의 q(query string)에 포함된 단어를 검색어로 사용한다. 검색 입력 창에 단어를 입력 후 엔터를 칠 경우, template의 method(GET)가 동작한다.

    title__icontains=query: 검색에 사용될 대상 필드를 찾기위해 사용되는 구문(Django-Field lookups). <필드명>__<Field lookups>로 구성된 키워드 인수이며, icontains 외에 필터 기능에 따라 여러가지 lookup이 존재한다. Field lookup 구문은 SQL에서 'WHERE'절에 해당한다.

    name='q': url에 사용될 query string을 담을 변수 명을 지정한다.

    {% empty %}: empty tag를 이용해 for 구문에서 조건에 맞는 객체를 찾지 못할 경우(비어있을 경우) 출력할 값 또는 문구를 지정할 수 있다.

     

    query string은 url에서 '?q=' 와 같이 구성되며 문자열을 q에 담아 view에 전달하는 방식이다.
    empty tag 실행

     

    댓글

Designed by Tistory.