Question

I'm trying to create an archive list with counts using SQLAlchemy, Flask and Jinja that looks like this. I don't want to show months that don't have posts. I can't figure out how to get the unique years, months and counts for each in a dictionary.

 2012 (11)
 - January (3)
 - February (5)
 - April (3)
 2013 (14)
 - April (2)
 - May (8)
 - June (2)
 - December
 2014 (13)
 - January (3)
 - February (8)
 - March (2)

model

class Post(db.Model):
    ''' Returns the Post table.

   '''
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(80))
    url = db.Column(db.String(120), unique=True)
    body = db.Column(db.Text)
    create_date = db.Column(db.DateTime)
    pub_date = db.Column(db.DateTime)
    pub_status = db.Column(db.Text(80))
    author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    topic_id = db.Column(db.Integer, db.ForeignKey('topic.id'))

    # relationships
    # one-to-many
    author = db.relationship('User', backref=db.backref('post', lazy='dynamic'))
    topic = db.relationship('Topic', backref=db.backref('post', lazy='dynamic'))

    # many-to-many
    tags = db.relationship('Tag', secondary=posts_tags, backref='posts', lazy='dynamic')

UPDATE

This code creates the dictionary, but not sure how I would add the counts. Any ideas? Is there a better approach?

views

# custom filter converts month number to "January", etc.
@app.template_filter('month_number')
def month_name(month_number):
    return calendar.month_name[month_number]

@app.route('/archive')
def display_archive():
    p = db.session.query(Post.pub_date).all()
    d = defaultdict(set) # removes the duplicate months by putting into sets.
    n = {}
    for i in p:
        d[i.pub_date.year].add(i.pub_date.month)
        d.items()

# first part returns eliminates dup months by making sets, needs sorting.
# defaultdict(<type 'set'>, {2012: set([1, 2, 4]),\
# 2013: set([4, 12, 5, 6]), 2014: set([1, 2, 3])})

for key, value in d.iteritems() :
        a = value
        a = list(a) #convert sets to list.
        a.sort()  # soft the list.
        n[key] = a

    return render_template('archive.html', d=d, n=n)

# convert to list for easy sorting.
#{2012: [1, 2, 4], 2013: [4, 5, 6, 12], 2014: [1, 2, 3]}

jinja2 template

    <dl>
    {% for key, value in n.iteritems() %}
        <dt>{{ key }}</dt>

        {% for v in value %}
           <dd><a href="/{{ key }}/{{ v }}">{{ v|month_number }}</a></dd>
        {% endfor %}
    {% endfor %}
    </dl>

Again, it works... but not sure how to add the counts.  It will have to be done before the defaultdict function I assume.
Was it helpful?

Solution

SOLUTION Here is the solution originally posted in the answer.

@app.route('/archive')
def display_archive():
    p = db.session.query(Post.pub_date).all()
    d = defaultdict(list)
    for i in p:
        d[i.pub_date.year].append(i.pub_date.month)

    # add the monthly counts by counting the instances of month number.
    adict = {}
    for k, v  in d.items():
        adict[k] = Counter(v)

    bdict = {}
    # add the monthly and yearly totals counts
    posttotal = 0
    for key, value in adict.iteritems():
        yearsum = 0
        for m, c in value.items():
            yearsum += c
            posttotal += c
            bdict[key] = yearsum

    d = defaultdict(list)
    for k, v in adict.items() + bdict.items():
        d[k].append(v)

    return render_template('archive.html', d=d)

jina2

{% extends "base.html" %}
{% block title %}Archive{% endblock %}
{% block content %}

<dl>
{% for key, value in d.iteritems() %}
    <dt><a href="/{{ key }}">{{ key }}</a> ({{ value[1] }})</dt>
    {% for m, c in value[0].items() %}
       <dd><a href="/{{ key }}/{{ m }}">{{m|month_number}}</a> ({{ c }})</a></dd>
        {{ a }}
    {% endfor %}
{% endfor %}
</dl>


{% endblock %}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top