Skip to content

Instantly share code, notes, and snippets.

@Flobin
Last active September 1, 2016 07:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Flobin/38bbd5fde27904e1ef4d5798eda0ab07 to your computer and use it in GitHub Desktop.
Save Flobin/38bbd5fde27904e1ef4d5798eda0ab07 to your computer and use it in GitHub Desktop.
<div class="article-form">
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<p class="article-form__error">
<strong>{{ error|escape }}</strong>
</p>
{% endfor %}
{% endfor %}
{% for error in form.non_field_errors %}
<p class="article-form__error">
<strong>{{ error|escape }}</strong>
</p>
{% endfor %}
{% endif %}
<form action="{% url 'article-submit' %}" method="POST">
{% csrf_token %}
{% for field in form %}
<fieldset class="article-form__field">
{% if field.name == "categories"%}
{{ field.label_tag }}
<ul id={{ field.auto_id }}>
{% for checkbox in field %}
<li>
{{ checkbox.tag }}
<label for="{{ checkbox.id_for_label }}">
{{ checkbox.choice_label }}
</label>
</li>
{% endfor %}
</ul>
{% elif field.name == "city" %}
{{ field.label_tag }}
<ul id={{ field.auto_id }}>
{% for radio in field %}
<li>
{{ radio.tag }}
<label for="{{ radio.id_for_label }}">
{{ radio.choice_label }}
</label>
</li>
{% endfor %}
</ul>
{% else %}
{{ field.label_tag }} {{ field }}
{% endif %}
</fieldset>
{% endfor %}
<fieldset class="article-form__field">
<input class="button" type="submit" value="save">
</fieldset>
</form>
</div>
{% extends 'articles/base.html' %}
{% block extra_head_stuff %}
{{ form.media }}
{% endblock extra_head_stuff %}
{% block article-submit %}
<h2 class="h3">Submit a new article</h2>
{% include 'articles/article-submit-form.html' %}
{% endblock article-submit %}
{% load staticfiles %}
<!doctype html>
<html class="no-js" lang="en-US">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Togethere</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
{% block extra_head_stuff %}
{% endblock extra_head_stuff %}
<script>
(function(d) {
var config = {
kitId: 'snd6kgu',
scriptTimeout: 3000,
async: true
},
h=d.documentElement,t=setTimeout(function(){h.className=h.className.replace(/\bwf-loading\b/g,"")+" wf-inactive";},config.scriptTimeout),tk=d.createElement("script"),f=false,s=d.getElementsByTagName("script")[0],a;h.className+=" wf-loading";tk.src='https://use.typekit.net/'+config.kitId+'.js';tk.async=true;tk.onload=tk.onreadystatechange=function(){a=this.readyState;if(f||a&&a!="complete"&&a!="loaded")return;f=true;clearTimeout(t);try{Typekit.load(config)}catch(e){}};s.parentNode.insertBefore(tk,s)
})(document);
</script>
<link rel="stylesheet" type="text/css" href="{% static 'articles/build/css/main.css' %}" />
</head>
<body id="body">
<header class="site-header">
<a href="/">
<img src="{% static 'articles/build/img/togethere-logo.svgz' %}" id="logo" alt="Click this togethere logo to go to the home page" role="img">
</a>
<nav role="navigation" class="top-nav">
<div class="top-nav__links">
<a href="{% url 'categories' %}">Categories</a>
<a href="{% url 'cities' %}">Cities</a>
</div>
<div id="auth" class="top-nav__links">
{% load account %}
{% if user.is_authenticated %}
You are signed in as: {% user_display user %} -
<a href="/accounts/logout/">log out</a>
{% else %}
<a href="/accounts/login/">log in</a> <a href="/accounts/register/">register</a>
{% endif %}
</div>
</nav>
</header>
<main class="content container">
{% block articles %}
{% endblock articles %}
{% block article %}
{% endblock article %}
{% block categories %}
{% endblock categories %}
{% block category %}
{% endblock category %}
{% block cities %}
{% endblock cities %}
{% block city %}
{% endblock city %}
{% block article-submit %}
{% endblock article-submit %}
{% block article-update %}
{% endblock article-update %}
{% block article-delete %}
{% endblock article-delete %}
{% block profile %}
{% endblock profile %}
</div>
<footer class="site-footer">
</footer>
<script src="{% static 'articles/build/lib/node_modules/pjax/pjax.js' %}"></script>
<script src="{% static 'articles/build/js/main.js' %}"></script>
</body>
</html>
static
|-src
| |-styles
| |-scripts
| |-images
|-build
| |-css
| |-js
| |-img
| |-lib
| | |-package.json
| | |-node_modules
| | | |-tinymce
| | | | |-tinymce.js
| | | | |-more_tinymce_files_here
|-node_modules
|-gulpfile.js
|-package.json
from django import forms
from django.forms import ModelForm
from tinymce.widgets import TinyMCE
from .models import Article, Category, City
TITLE_LENGTH_ERROR = "This title is too long, please make it 200 characters or less."
TITLE_EMPTY_ERROR = "You’ll have to add a title."
TEXT_EMPTY_ERROR = "Please enter some text."
NO_CATEGORY_ERROR = "Please select a category."
NO_CITY_ERROR = "Please select a city."
class ArticleForm(ModelForm):
text = forms.CharField(widget=TinyMCE(attrs={'cols': 80, 'rows': 30}))
class Meta:
model = Article
fields = ['title', 'text', 'categories', 'city']
widgets = {'title': forms.TextInput(attrs={
'placeholder': 'Enter a descriptive title'}),
'categories': forms.CheckboxSelectMultiple(choices=Category.CATEGORY_CHOICES),
'city': forms.RadioSelect(choices=City.CITY_CHOICES),
}
error_messages = {
'title': {
'max_length': TITLE_LENGTH_ERROR,
'required': TITLE_EMPTY_ERROR,
},
'text': {
'required': TEXT_EMPTY_ERROR,
},
'categories': {
'required': NO_CATEGORY_ERROR,
},
'city': {
'required': NO_CITY_ERROR,
}
}
"""
Django settings for togethere project.
Generated by 'django-admin startproject' using Django 1.9.7.
For more information on this file, see
https://docs.djangoproject.com/en/1.9/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.9/ref/settings/
"""
import os
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'br&avzw7u$$c+xz&&z#e#$n3(0k4&z_m__#p%xxwc@a5$g*gi3'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
'articles',
'allauth',
'allauth.account',
'allauth.socialaccount',
'allauth.socialaccount.providers.facebook',
'tinymce',
]
MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'togethere.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
AUTHENTICATION_BACKENDS = (
# Needed to login by username in Django admin, regardless of `allauth`
'django.contrib.auth.backends.ModelBackend',
# `allauth` specific authentication methods, such as login by e-mail
'allauth.account.auth_backends.AuthenticationBackend',
)
# also for allauth
SITE_ID = 2
# disable this in production
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
LOGIN_REDIRECT_URL = '/'
ACCOUNT_EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL=None
ACCOUNT_EMAIL_REQUIRED=True
ACCOUNT_AUTHENTICATION_METHOD="email"
ACCOUNT_EMAIL_VERIFICATION = "optional"
# allauth end
WSGI_APPLICATION = 'togethere.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.9/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
# Password validation
# https://docs.djangoproject.com/en/1.9/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/1.9/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'Europe/Amsterdam'
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.9/howto/static-files/
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
os.environ['DJANGO_LIVE_TEST_SERVER_ADDRESS'] = 'localhost:8123'
# TinyMCE configuration
TINYMCE_JS_URL = os.path.join(STATIC_URL, "articles/build/lib/node_modules/tinymce/tinymce.min.js")
TINYMCE_JS_ROOT = os.path.join(STATIC_ROOT, "articles/build/lib/node_modules/tinymce")
TINYMCE_COMPRESSOR = False
TINYMCE_DEFAULT_CONFIG = {
'theme': "modern",
'toolbar': "undo redo | bold italic | bullist numlist | blockquote | removeformat",
'menubar': False,
'statusbar': False,
'schema': "html5",
'max_height': 500,
'max_width': 500,
'min_height': 100,
'min_width': 400
}
"""togethere URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/1.9/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: url(r'^$', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.conf.urls import url, include
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import include, url
from django.contrib import admin
from allauth.account import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r"^accounts/register/$", views.signup, name="account_signup"),
url(r'^accounts/', include('allauth.urls')),
url(r'^tinymce/', include('tinymce.urls')),
url(r'', include('articles.urls')),
]
from django.shortcuts import render, get_object_or_404
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse, reverse_lazy
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from .models import Article, Category, City, Profile
from .forms import ArticleForm
class ArticlesView(ListView):
model = Article
context_object_name = 'articles'
paginate_by = 25
template_name = 'articles/articles.html'
def get_context_data(self, **kwargs):
context = super(ArticlesView, self).get_context_data(**kwargs)
context['form'] = ArticleForm
return context
class ArticleView(DetailView):
model = Article
template_name = 'articles/article.html'
class ArticleSubmitView(CreateView):
model = Article
form_class = ArticleForm
template_name = 'articles/article-submit.html'
# set up login required later
# @method_decorator(login_required)
# def dispatch(self, *args, **kwargs):
# return super(ArticleSubmitView, self).dispatch(*args, **kwargs)
def form_valid(self, form):
obj = form.save(commit=False)
obj.author = self.request.user
obj.save()
form.save_m2m()
return HttpResponseRedirect(reverse('article', kwargs={'pk': obj.id}))
class ArticleUpdateView(UpdateView):
model = Article
form_class = ArticleForm
template_name_suffix = '-update'
def form_valid(self, form):
obj = form.save(commit=False)
obj.author = self.request.user
obj.save()
form.save_m2m()
return HttpResponseRedirect(reverse('article', kwargs={'pk': obj.id}))
class ArticleDeleteView(DeleteView):
model = Article
success_url = reverse_lazy('articles')
template_name_suffix = '-delete'
class CategoriesView(ListView):
model = Category
context_object_name = 'categories'
template_name = 'articles/categories.html'
class CategoryView(ListView):
model = Article
context_object_name = 'articles'
paginate_by = 25
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(CategoryView, self).get_context_data(**kwargs)
# Add in a QuerySet of the category
context['category'] = Category.objects.get(pk=self.kwargs['pk'])
return context
def get_queryset(self):
return Article.objects.filter(categories=self.kwargs['pk'])
template_name = 'articles/category.html'
class CitiesView(ListView):
model = City
context_object_name = 'cities'
template_name = 'articles/cities.html'
class CityView(ListView):
model = Article
context_object_name = 'articles'
paginate_by = 25
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(CityView, self).get_context_data(**kwargs)
# Add in a QuerySet of the city
context['city'] = City.objects.get(pk=self.kwargs['pk'])
return context
def get_queryset(self):
return Article.objects.filter(city=self.kwargs['pk'])
template_name = 'articles/city.html'
class ProfileView(ListView):
model = Article
context_object_name = 'articles'
paginate_by = 25
def get_context_data(self, **kwargs):
context = super(ProfileView, self).get_context_data(**kwargs)
context['profile'] = get_object_or_404(Profile, pk=self.kwargs['pk'])
return context
def get_queryset(self):
return Article.objects.filter(author__id=self.kwargs['pk'])
template_name = 'articles/profile.html'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment