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>
   
{{ 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
복사