Add created/changed datetime support, and improve metadata on stores.

* Added schema.org metadata for stores.
This commit is contained in:
2013-04-11 18:11:58 +01:00
parent f3a2e2c8bd
commit 934062aa46
4 changed files with 259 additions and 72 deletions

View File

@@ -4,6 +4,7 @@ from django.shortcuts import render_to_response
from django import forms
from django.db import transaction
from django.db.models import Count
from django.utils.timezone import now
from django.contrib import admin
from django.contrib.contenttypes.generic import GenericStackedInline
from .models import Chain, Store, Address, Brand, ClaimRequest, Link, LinkType, County, Country
@@ -16,7 +17,7 @@ class LinkInlineAdmin(GenericStackedInline):
class ChainAdmin(admin.ModelAdmin):
list_filter = ['active']
list_display = ['name']
list_display = ['name', 'active', 'changed']
prepopulated_fields = {"slug": ("name",)}
search_fields = ['name']
inlines = [
@@ -25,7 +26,7 @@ class ChainAdmin(admin.ModelAdmin):
class StoreAdmin(admin.ModelAdmin):
list_filter = ['chain', 'active']
list_display = ['name', 'store_type', 'active']
list_display = ['name', 'store_type', 'active', 'changed']
prepopulated_fields = {"slug": ("name",)}
search_fields = ['name']
inlines = [
@@ -39,7 +40,7 @@ class StoreAdmin(admin.ModelAdmin):
def set_active(self, request, queryset):
with transaction.commit_on_success():
queryset.update(active=True)
queryset.update(active=True, changed=now())
self.message_user(request, "Successfully set %d stores to active." % queryset.count())
set_active.short_description = 'Set selected stores active.'
@@ -53,6 +54,7 @@ class StoreAdmin(admin.ModelAdmin):
with transaction.commit_on_success():
for store in queryset:
store.brands.add(brand)
queryset.update(changed=now())
if count > 1:
plural = 's'
else:
@@ -61,7 +63,7 @@ class StoreAdmin(admin.ModelAdmin):
return HttpResponseRedirect(request.get_full_path())
if not form:
form = self.AddBrandForm(initial={'_selected_action': queryset.values_list('id', flat=True)})
return render_to_response('admin/add_brand.html', {'stores': queryset, 'brand_form': form }, RequestContext(request))
return render_to_response('admin/add_brand.html', {'stores': queryset, 'brand_form': form}, RequestContext(request))
add_brand.short_description = "Add brand to the selected stores"
@@ -77,9 +79,7 @@ class StoreAdmin(admin.ModelAdmin):
count = queryset.count()
chain = form.cleaned_data['chain']
with transaction.commit_on_success():
for store in queryset:
store.chain = chain
store.save()
queryset.update(chain=chain, changed=now())
if count > 1:
plural = 's'
else:
@@ -101,6 +101,7 @@ class ClaimAdmin(admin.ModelAdmin):
def approve_request(self, request, queryset):
qs = queryset.filter(status=ClaimRequest.CLAIM_STATUS_PENDING)
with transaction.commit_on_success():
for obj in qs:
obj.status = ClaimRequest.CLAIM_STATUS_APPROVED
target = obj.generic_obj
@@ -139,9 +140,7 @@ class CountyAdmin(admin.ModelAdmin):
if form.is_valid():
country = form.cleaned_data['country']
with transaction.commit_on_success():
for county in queryset:
county.country = country
county.save()
queryset.update(country=country)
self.message_user(request, "Successfully set %d counties to %s." % (queryset.count(), country))
return HttpResponseRedirect(request.get_full_path())
if not form:

View File

@@ -0,0 +1,172 @@
# -*- coding: utf-8 -*-
from django.utils.timezone import now
from south.db import db
from south.v2 import SchemaMigration
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'Chain.created'
db.add_column(u'stores_chain', 'created',
self.gf('django.db.models.fields.DateTimeField')(default=now),
keep_default=False)
# Adding field 'Chain.changed'
db.add_column(u'stores_chain', 'changed',
self.gf('django.db.models.fields.DateTimeField')(default=now),
keep_default=False)
# Adding field 'Store.created'
db.add_column(u'stores_store', 'created',
self.gf('django.db.models.fields.DateTimeField')(default=now),
keep_default=False)
# Adding field 'Store.changed'
db.add_column(u'stores_store', 'changed',
self.gf('django.db.models.fields.DateTimeField')(default=now),
keep_default=False)
def backwards(self, orm):
# Deleting field 'Chain.created'
db.delete_column(u'stores_chain', 'created')
# Deleting field 'Chain.changed'
db.delete_column(u'stores_chain', 'changed')
# Deleting field 'Store.created'
db.delete_column(u'stores_store', 'created')
# Deleting field 'Store.changed'
db.delete_column(u'stores_store', 'changed')
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'stores.address': {
'Meta': {'object_name': 'Address'},
'address1': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'address2': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
'address3': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}),
'city': ('django.db.models.fields.CharField', [], {'max_length': "'50'"}),
'country': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'addresses'", 'to': "orm['stores.Country']"}),
'county': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'addresses'", 'null': 'True', 'to': "orm['stores.County']"}),
'geo_latitude': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
'geo_longitude': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'postcode': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'})
},
'stores.brand': {
'Meta': {'ordering': "['name']", 'object_name': 'Brand'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
},
'stores.chain': {
'Meta': {'ordering': "['name']", 'object_name': 'Chain'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'changed': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'editor': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'editable_chains'", 'null': 'True', 'to': u"orm['auth.User']"}),
'head_office': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['stores.Address']", 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'long_description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '200', 'blank': 'True'}),
'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
},
'stores.claimrequest': {
'Meta': {'object_name': 'ClaimRequest'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'note': ('django.db.models.fields.TextField', [], {}),
'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
'object_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'claims'", 'to': u"orm['auth.User']"})
},
'stores.country': {
'Meta': {'ordering': "['name']", 'object_name': 'Country'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'iso_code': ('django.db.models.fields.CharField', [], {'max_length': '3'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'stores.county': {
'Meta': {'ordering': "['name']", 'object_name': 'County'},
'country': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'counties'", 'null': 'True', 'to': "orm['stores.Country']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'stores.link': {
'Meta': {'object_name': 'Link'},
'account_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'account_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['stores.LinkType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
'object_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"})
},
'stores.linktype': {
'Meta': {'ordering': "['name']", 'object_name': 'LinkType'},
'icon': ('django.db.models.fields.CharField', [], {'default': "'icon-globe'", 'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'url_format': ('django.db.models.fields.CharField', [], {'max_length': '255'})
},
'stores.store': {
'Meta': {'ordering': "['name']", 'object_name': 'Store'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'address': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'stores'", 'to': "orm['stores.Address']"}),
'brands': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'stores'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['stores.Brand']"}),
'chain': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'stores'", 'null': 'True', 'to': "orm['stores.Chain']"}),
'changed': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'editor': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'editable_stores'", 'null': 'True', 'to': u"orm['auth.User']"}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'long_description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'phone': ('django.db.models.fields.CharField', [], {'max_length': '25', 'null': 'True', 'blank': 'True'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '200', 'blank': 'True'}),
'store_type': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
}
}
complete_apps = ['stores']

View File

@@ -1,6 +1,7 @@
import re
from django.db import models
from django.core.urlresolvers import reverse_lazy
from django.utils.timezone import now
from django.contrib.auth import get_user_model
from django.contrib.gis.geos import Point
from django.contrib.contenttypes.models import ContentType
@@ -21,9 +22,13 @@ class Chain(models.Model):
long_description = models.TextField('Description', null=True, blank=True)
links = generic.GenericRelation('stores.Link', content_type_field='object_type')
created = models.DateTimeField('Created Date/Time', default=now)
changed = models.DateTimeField('Changed Date/Time', default=now)
def save(self, **kwargs):
if self.slug == '':
self.slug = re.sub(r'\W+', '-', str(self.name).lower())
self.changed = now()
return super(Chain, self).save(**kwargs)
def get_absolute_url(self):
@@ -65,6 +70,9 @@ class Store(models.Model):
long_description = models.TextField('Description', null=True, blank=True, help_text="Full description of the store, including any marketing material. Markdown supported.")
brands = models.ManyToManyField('stores.Brand', related_name='stores', null=True, blank=True, help_text="Brands that are sold by this store.")
created = models.DateTimeField('Created Date/Time', default=now)
changed = models.DateTimeField('Changed Date/Time', default=now)
def get_full_address(self):
if self.address:
return self.address.full_address
@@ -76,6 +84,7 @@ class Store(models.Model):
def save(self, **kwargs):
if not self.slug or self.slug == '':
self.slug = re.sub(r'\W+', '-', str(self.name).lower())
self.changed = now()
return super(Store, self).save(**kwargs)
def get_absolute_url(self):

View File

@@ -9,7 +9,7 @@
{% block style %}
<style type="text/css" xmlns="http://www.w3.org/1999/html">
.vcard ul {
.store-details ul {
list-style-type: none;
}
#map-canvas-store {
@@ -45,14 +45,17 @@ $(document).ready(function(){initialize_map_store()});
{% endblock %}
{% block content %}
<div class="page-header">
<h1>{{ store.name }}</h1>
</div>
<div itemscope itemtype="http://schema.org/Place">
<div class="page-header">
<h1 itemtype="name">{{ store.name }}</h1>
</div>
<div class="row-fluid">
<div class="row-fluid">
<div class="span8">
{% if store.long_description %}
<div itemprop="description">
{{ store.long_description|markdown }}
</div>
{% else %}
<p>No description / write up available for this store at the moment.</p>
{% endif %}
@@ -78,36 +81,40 @@ $(document).ready(function(){initialize_map_store()});
{% endif %}
{% if store.chain %}<p><b>Chain</b>: <a href="{% url "chain-detail" store.chain.slug %}">{{ store.chain }}</a></p>{% endif %}
<p><b>Type</b>: {{ store.get_store_type_display }}</p>
<div class="vcard">
<div class="store-details">
<h3>Address</h3>
<ul class="adr">
<li class="fn">{{ store.name }}</li>
<li class="street-address">{{ store.address.address1 }}</li>
<ul itemscope itemtype="http://schema.org/PostalAddress">
<li itemprop="name">{{ store.name }}</li>
<li itemprop="streetAddress">{{ store.address.address1 }}</li>
{% if store.address.address2 %}<li>{{ store.address.address2 }}</li>{% endif %}
{% if store.address.address3 %}<li>{{ store.address.address3 }}</li>{% endif %}
<li class="locality">{{ store.address.city }}</li>
{% if store.address.county %}<li class="region">{{ store.address.county }}</li>{% endif %}
<li class="postal-code">{{ store.address.postcode }}</li>
<li class="country-name">{{ store.address.country }}</li>
<li itemprop="addressLocality">{{ store.address.city }}</li>
{% if store.address.county %}<li itemprop="addressRegion">{{ store.address.county }}</li>{% endif %}
<li itemprop="postalCode">{{ store.address.postcode }}</li>
<li itemprop="addressCountry">{{ store.address.country }}</li>
</ul>
<h3>Contact Details</h3>
<ul>
{% if store.website %}<li>Website: <a class="url" target="_new" href="{{ store.website }}">{{ store.website }}</a></li>{% endif %}
{% if store.email %}<li>Email: <a class="email" href="mailto:{{ store.email }}">{{ store.email }}</a></li>{% endif %}
{% if store.phone %}<li>Phone: <span class="tel">{{ store.phone }}</span></li>{% endif %}
<ul itemscope itemtype="http://schema.org/ContactPoint">
{% if store.website %}<li>Website: <a target="_new" href="{{ store.website }}">{{ store.website }}</a></li>{% endif %}
{% if store.email %}<li>Email: <a itemprop="email" href="mailto:{{ store.email }}">{{ store.email }}</a></li>{% endif %}
{% if store.phone %}<li>Phone: <span itemprop="telephone">{{ store.phone }}</span></li>{% endif %}
{% for link in store.links.all %}
<li>{{ link.to_html|safe }}</li>
{% endfor %}
</ul>
</div>
<div id="map-canvas-store" style="width: 300px; height: 300px;" class="map">
<noscript>
<img alt="Map of {{ store.address.full_address }}" src="https://maps.google.com/maps/api/staticmap?center={{ store.address.geo_latitude }},{{ store.address.geo_longitude }}&zoom=16&markers={{ store.address.geo_latitude }},{{ store.address.geo_longitude }}&size=300x300&sensor=false">
</noscript>
</div>
<p class="muted">Last Updated: {{ store.changed }}</p>
<p class="hidden" itemprop="geocoordinates"><span class="latitude">{{ store.address.geo_latitude }}</span>, <span class="longitude">{{ store.address.geo_longitude }}</span></p>
</div>
</div>
{% flag_form store %}
</div>
</div>
{% endblock %}