📢 공지사항
home
🍒

정하은_4, 5주차

31강_Profileapp 구현 시작
31강_Profileapp 구현 시작

05. Articleapp Implementation

05. Articleapp Implementation
34강_MagicGrid 소개 및 Articleapp 시작
urls.py
from django.urls import path from django.views.generic import TemplateView urlpatterns = [ path('list/', TemplateView.as_view(template_name='articleApp/list.html'), name='list'), ]
Python
복사
list.html
{% extends 'base.html' %} {% load static %} {% block content %} <style> .container div { width: 250px; background-color: antiquewhite; display: flex; justify-content: center; align-items: center; border-radius: 1rem; } <!-- 추가 변경함 --> .container img { width: 100%; border-radius: 1rem; } </style> <!-- 추가 변경함 --> <div class="container"> <div class="item1"> <img src="https://picsum.photos/200/300" alt=""> </div> <div class="item1"> <img src="https://picsum.photos/200/410" alt=""> </div> <div class="item1"> <img src="https://picsum.photos/200/500" alt=""> </div> <div class="item1"> <img src="https://picsum.photos/200/100" alt=""> </div> <div class="item1"> <img src="https://picsum.photos/200/300" alt=""> </div> <div class="item1"> <img src="https://picsum.photos/200/300" alt=""> </div> <div class="item1"> <img src="https://picsum.photos/200/320" alt=""> </div> <div class="item1"> <img src="https://picsum.photos/200/298" alt=""> </div> <div class="item1"> <img src="https://picsum.photos/200/100" alt=""> </div> <div class="item1"> <img src="https://picsum.photos/200/425" alt=""> </div> <div class="item1"> <img src="https://picsum.photos/200/344" alt=""> </div> <div class="item1"> <img src="https://picsum.photos/200/122" alt=""> </div> <div class="item1"> <img src="https://picsum.photos/200/398" alt=""> </div> </div> <!-- 추가 변경함 --> <script src="{% static 'js/magicgrid.js' %}"> </script> {% endblock %}
HTML
복사
magicgrid.js
'use strict'; /** * @author emmanuelolaojo * @since 11/11/18 */ /** * Validates the configuration object. * * @param config - configuration object */ var checkParams = function (config) { var DEFAULT_GUTTER = 25; var booleanProps = ["useTransform", "center"]; if (!config) { throw new Error("No config object has been provided."); } for(var prop of booleanProps){ if(typeof config[prop] !== "boolean"){ config[prop] = true; } } if(typeof config.gutter !== "number"){ config.gutter = DEFAULT_GUTTER; } if (!config.container) { error("container"); } if (!config.items && !config.static) { error("items or static"); } }; /** * Handles invalid configuration object * errors. * * @param prop - a property with a missing value */ var error = function (prop) { throw new Error(("Missing property '" + prop + "' in MagicGrid config")); }; /** * Finds the shortest column in * a column list. * * @param cols - list of columns * * @return shortest column */ var getMin = function (cols) { var min = cols[0]; for (var col of cols) { if (col.height < min.height) { min = col; } } return min; }; /** * @author emmanuelolaojo * @since 11/10/18 * * The MagicGrid class is an * implementation of a flexible * grid layout. */ var MagicGrid = function MagicGrid (config) { checkParams(config); if (config.container instanceof HTMLElement) { this.container = config.container; this.containerClass = config.container.className; } else { this.containerClass = config.container; this.container = document.querySelector(config.container); } this.static = config.static || false; this.size = config.items; this.gutter = config.gutter; this.maxColumns = config.maxColumns || false; this.useMin = config.useMin || false; this.useTransform = config.useTransform; this.animate = config.animate || false; this.center = config.center; this.styledItems = new Set(); }; /** * Initializes styles * * @private */ MagicGrid.prototype.initStyles = function initStyles () { if (!this.ready()) { return; } this.container.style.position = "relative"; var items = this.items(); for (var i = 0; i < items.length; i++) { if (this.styledItems.has(items[i])) { continue; } var style = items[i].style; style.position = "absolute"; if (this.animate) { style.transition = (this.useTransform ? "transform" : "top, left") + " 0.2s ease"; } this.styledItems.add(items[i]); } }; /** * Gets a collection of all items in a grid. * * @return {HTMLCollection} * @private */ MagicGrid.prototype.items = function items () { return this.container.children; }; /** * Calculates the width of a column. * * @return width of a column in the grid * @private */ MagicGrid.prototype.colWidth = function colWidth () { return this.items()[0].getBoundingClientRect().width + this.gutter; }; /** * Initializes an array of empty columns * and calculates the leftover whitespace. * * @return {{cols: Array, wSpace: number}} * @private */ MagicGrid.prototype.setup = function setup () { var width = this.container.getBoundingClientRect().width; var colWidth = this.colWidth(); var numCols = Math.floor(width/colWidth) || 1; var cols = []; if (this.maxColumns && numCols > this.maxColumns) { numCols = this.maxColumns; } for (var i = 0; i < numCols; i++) { cols[i] = {height: 0, index: i}; } var wSpace = width - numCols * colWidth + this.gutter; return {cols: cols, wSpace: wSpace}; }; /** * Gets the next available column. * * @param cols list of columns * @param i index of dom element * * @return {*} next available column * @private */ MagicGrid.prototype.nextCol = function nextCol (cols, i) { if (this.useMin) { return getMin(cols); } return cols[i % cols.length]; }; /** * Positions each item in the grid, based * on their corresponding column's height * and index then stretches the container to * the height of the grid. */ MagicGrid.prototype.positionItems = function positionItems () { var ref = this.setup(); var cols = ref.cols; var wSpace = ref.wSpace; var maxHeight = 0; var colWidth = this.colWidth(); var items = this.items(); wSpace = this.center ? Math.floor(wSpace / 2) : 0; this.initStyles(); for (var i = 0; i < items.length; i++) { var col = this.nextCol(cols, i); var item = items[i]; var topGutter = col.height ? this.gutter : 0; var left = col.index * colWidth + wSpace + "px"; var top = col.height + topGutter + "px"; if(this.useTransform){ item.style.transform = "translate(" + left + ", " + top + ")"; } else{ item.style.top = top; item.style.left = left; } col.height += item.getBoundingClientRect().height + topGutter; if(col.height > maxHeight){ maxHeight = col.height; } } this.container.style.height = maxHeight + this.gutter + "px"; }; /** * Checks if every item has been loaded * in the dom. * * @return {Boolean} true if every item is present */ MagicGrid.prototype.ready = function ready () { if (this.static) { return true; } return this.items().length >= this.size; }; /** * Periodically checks that all items * have been loaded in the dom. Calls * this.listen() once all the items are * present. * * @private */ MagicGrid.prototype.getReady = function getReady () { var this$1 = this; var interval = setInterval(function () { this$1.container = document.querySelector(this$1.containerClass); if (this$1.ready()) { clearInterval(interval); this$1.listen(); } }, 100); }; /** * Positions all the items and * repositions them whenever the * window size changes. */ MagicGrid.prototype.listen = function listen () { var this$1 = this; if (this.ready()) { var timeout; window.addEventListener("resize", function () { if (!timeout){ timeout = setTimeout(function () { this$1.positionItems(); timeout = null; }, 200); } }); this.positionItems(); } else { this.getReady(); } }; let magicGrid = new MagicGrid({ container: '.container', animate: true, gutter: 30, static: true, useMin: true }); magicGrid.listen(); // java 추가 var masonrys = document.getElementByTagName("img"); for (let i = 0; i < masonrys.length; i++){ masonrys[i].addEventListener('load', function (){ magicGrid.positionItems(); }, false); }
JavaScript
복사
35강_Article 모델 생성 오류 수정
36강_Articleapp 구현
views.py
class ArticleListView(ListView): model = Article context_object_name = 'article_list' template_name = 'articleApp/list.html' paginate_by = 3
Python
복사
urls.py
urlpatterns = [ path('list/', ArticleListView.as_view(), name='list'), ... ]
Python
복사
list.html
</style> <!-- #3.--> {% if article_list %} <div class="container"> {% for article in article_list %} <a href="{% url 'articleApp:detail' pk=article.pk %}"> {% include 'snippets/card.html' with article=article %} </a> {% endfor %} </div> <script src="{% static 'js/magicgrid.js' %}"> </script> {% else %} <div class="text-center"> <h1>No Articles YET!</h1> </div> {% endif %} {% include 'snippets/pagination.html' with page_obj=page_obj %} <div style="text-align: center"> <a href="{% url 'articleApp:create' %}" class="btn btn-dark rounded-pill col-3 mt-3"> Create Article </a> </div> {% endblock %}
HTML
복사
card.html
<div> <img src="{{ article.image.url }}" alt=""> </div>
HTML
복사
pagination.html
<div style="text-align: center; margin: 1rem 0;"> {% if page_obj.has_previous %} <a href="{% url 'articleApp:list' %}?page={{ page_obj.previous_page_number }}" class="btn btn-secondary rounded-pill"> {{ page_obj.previous_page_number }} </a> {% endif %} <a href="{% url 'articleApp:list' %}?page={{ page_obj.number }}" class = "btn btn-secondary rounded-pill active"> {{ page_obj.number }} </a> {% if page_obj.has_next %} <a href="{% url 'articleApp:list' %}?page={{ page_obj.next_page_number }}" class="btn btn-secondary rounded-pill"> {{ page_obj.next_page_number }} </a> {% endif %} </div>
HTML
복사
37강_ListView, Pagination 소개 및 적용
details.html
<div style="border: 1px solid; text-align: left; padding: 4%; margin: 1rem 0; border-radius: 1rem; border-color: #bbb;"> <div> <strong> {{ comment.writer.profile.nickname }} </strong> &nbsp&nbsp&nbsp {{ comment.created_at }} </div> <div style="margin: 1rem 0;"> {{ comment.content }} </div> {% if comment.writer == user %} <div style="text-align: right"> <a href="{% url 'commentapp:delete' pk=comment.pk %}" class="btn btn-danger rounded-pill"> Delete </a> </div> {% endif %} </div>
HTML
복사
06. Commentapp Implementation
38강_Mixin 소개 및 Commentapp 시작
38강_Commentapp 마무리

07. Mobile Responsive Layout

07. Commentapp Implementation
40강_모바일 디버깅, 반응형 레이아웃

08. Projectapp Implementation

08. Projectapp Implementation
41강_ProjectApp 구현
42강_MultipleObjectMixin을 통한 ProjectApp 마무리
def get_context_data(self, **kwargs): object_list = Article.objects.filter(project=self.get_object()) return super(ProjectDetailView, self).get_context_data(object_list=object_list, **kwargs)
Python
복사
detail.html
{% include 'snippets/list_fragment.html' with article_list=object_list %}
HTML
복사
41강_ProjectApp 구현

09. Subscribeapp Implementation

09. Subscribeapp Implementation
43강_RedirectView를 통한 SubscribeApp 시작
class Subscription(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='subscription') project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name='subscription') class Meta: unique_together = ('user', 'project')
Python
복사
@method_decorator(login_required, 'get') class SubscriptionView(RedirectView): def get_redirect_url(self, *args, **kwargs): return reverse('projectapp:detail', kwargs={'pk': self.request.GET.get('project_pk')}) def get(self, request, *args, **kwargs): project = get_object_or_404(Project, pk=self.request.GET.get('project_pk')) #project pk를 갖고 있는 project를 찾는데 없으면 페이지를 찾을 수 없다는 response를 되돌려 준다 user = self.request.user #위의 2가지 정보로 subscription을 찾음 subscription = Subscription.objects.filter(user=user, project=project) #구독정보가 있다면 지우고 없다면 만든다 if subscription.exists(): subscription.delete() else: Subscription(user=user, project=project).save() return super(SubscriptionView, self).get(request, *args, **kwargs)
Python
복사
views,py
def get_context_data(self, **kwargs): project = self.object user = self.request.user if user.is_authenticated: subscription = Subscription.objects.filter(user=user, project=project) object_list = Article.objects.filter(project=self.get_object()) return super(ProjectDetailView, self).get_context_data(object_list=object_list, subscription=subscription, **kwargs)
Python
복사
44강_FIeld Lookup을 사용한 구독 페이지 구현
@method_decorator(login_required,'get') class SubscriptionListView(ListView): model = Article context_object_name = 'article/list' template_name = 'subscribeapp/list.html' paginate_by = 5
Python
복사