Нашли опечатку?

Выделите её мышкой и нажмите Enter

Ctrl-Enter
Выполнено:
87 1 199 25
Всего пользователей: 1143

Документация Django на русском

Документация на русском языке для Django стала реальностью. Благодаря новым возможностям движка Sphinx мы можем оперативно дополнять перевод, по мере обновления оригинальной документации.

Нужна помощь дизайнера

Хотим добавить ачивки на сайте. Они уже как бы есть, но нет красивых иконок для отображения в профиле и на сайте. Если кто может быстренько и без напряга сделать с десяток, мы были бы благодарны. Обсуждение на форуме.

<<< Вернуться

Первый сниппет. Собственно кусок этого приложения.

17 января 2013 г. 20:20:09

src/code_review/models.py

from ..accounts.models import User
from django.db import models
from django.utils.html import escape
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from django.utils import dateformat
from django.conf import settings
import markdown


class Snipet(models.Model):
    LANGUAGE_CHOICES = (
        ('python', u'Python'),
        ('javascript', u'JavaScript')
    )
    title = models.CharField(_(u'title'), max_length=255)
    description = models.TextField(_(u'description'), blank=True)
    language = models.CharField(_(u'language'), default='python', choices=LANGUAGE_CHOICES, max_length=100,
        help_text=_(u'Main snippet language'))
    author = models.ForeignKey(User, verbose_name=_(u'author'))
    created = models.DateTimeField(_(u'created'), auto_now_add=True)

    class Meta:
        ordering = ['-created']

    def __unicode__(self):
        return self.title

    @models.permalink
    def get_absolute_url(self):
        return ('code_review:details', [self.pk], {})


class File(models.Model):
    LANGUAGE_CHOICES = (
        ('python', u'Python'),
        ('javascript', u'JavaScript')
    )
    name = models.CharField(_(u'file name'), max_length=1000)
    content = models.TextField(_(u'content'))
    snipet = models.ForeignKey(Snipet)
    language = models.CharField(_(u'language'), blank=True, max_length=100, choices=LANGUAGE_CHOICES)

    def __unicode__(self):
        return self.name


class Comment(models.Model):
    file = models.ForeignKey(File, verbose_name=_(u'file'))
    row = models.PositiveIntegerField(_(u'row'))
    content = models.TextField(_(u'comment'), max_length=1000)
    author = models.ForeignKey(User, verbose_name=_(u'author'), related_name='code_comments')
    created = models.DateTimeField(_(u'created'), auto_now_add=True)

    class Meta:
        ordering = ['-created']

    def __unicode__(self):
        return u"%s: %s..." % (self.author, self.content[:50])

    def get_content(self):
        return mark_safe(markdown.markdown(escape(self.content)).replace('\n', '<br />'))

    def get_json(self):
        return {
            'id': self.pk,
            'row': self.row,
            'content': self.get_content(),
            'author': unicode(self.author),
            'created': dateformat.format(self.created, settings.DATETIME_FORMAT),
            'author_avatar': self.author.avatar()
        }

src/code_review/views.py

from ..decorators import render_to
from .forms import CommentForm, AddSnipetForm, FileFormset
from .models import Snipet, Comment, File
from django.contrib.auth.decorators import login_required
from django.core.serializers.json import DjangoJSONEncoder
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.shortcuts import redirect
from django.utils import simplejson
from django.views.generic.base import View
from django.views.generic.list_detail import object_list
from django.utils.translation import ugettext_lazy as _


def index(request):
    qs = Snipet.objects.all()
    extra_context = {}
    return object_list(request, qs, 20,
                       template_name='code_review/index.html',
                       extra_context=extra_context)


@login_required
@render_to('code_review/add.html')
def add(request):
    form = AddSnipetForm(request.user, request.POST or None)

    if form.is_valid():
        obj = form.save(commit=False)
        form_validated = True
    else:
        obj = Snipet()
        form_validated = False

    file_formset = FileFormset(request.POST or None, instance=obj)

    if file_formset.is_valid() and form_validated:
        if not any(f.has_changed() for f in file_formset.forms):
            form._errors['__all__'] = form.error_class([_(u'Add at least one file')])
        else:
            obj.save()
            file_formset.save()
            return redirect(obj)

    return {
        'form': form,
        'file_formset': file_formset
    }


@render_to('code_review/details.html')
def details(request, pk):
    obj = get_object_or_404(Snipet, pk=pk)
    return {
        'object': obj
    }


class CommentsApi(View):

    def get(self, request, file_id):
        f = get_object_or_404(File, pk=file_id)
        return self.response([comment.get_json() for comment in f.comment_set.all()])

    def post(self, request, file_id):
        f = get_object_or_404(File, pk=file_id)

        if not request.user.is_authenticated():
            return HttpResponse('Authentication required', status=400)

        try:
            data = simplejson.loads(request.read())
        except simplejson.JSONDecodeError:
            return HttpResponse('JSON decode error', status=400)

        form = CommentForm(f, request.user, data)
        if form.is_valid():
            obj = form.save()
            return self.response(obj.get_json())
        else:
            return HttpResponse('Invalid data', status=400)

    def response(self, content):
        json = simplejson.dumps(content, cls=DjangoJSONEncoder)
        return HttpResponse(json, mimetype="application/json")

comments_api = CommentsApi.as_view()

static/code_review/js/base.js

var CodeReview = {};

CodeReview.Comment = Backbone.Model.extend({

});

CodeReview.CommentList = Backbone.Collection.extend({

    model: CodeReview.Comment,

    initialize: function(options) {
        this.url = options.url;
    }
});

CodeReview.CommentView = Backbone.View.extend({
    tagName: 'div',
    className: 'comment media',
    template: _.template(
        '<a class="pull-left" href="#"><img class="media-object" src="<%= author_avatar %>"></a>'+
        '<div class="media-body"><em class="created"><%= created %></em><br><%= content %></div>'
    ),

    initialize: function(options) {
        this.listenTo(this.model, 'destroy', this.remove);
        this.$placeholder = $('<div class="comment-placeholder"></div>');
    },

    getPlaceholder: function(){
        this.$placeholder.attr('style', 'height: '+(this.$el.outerHeight()+1)+'px !important');
        return this.$placeholder;
    },

    render: function(){
        this.$el.html(this.template(this.model.toJSON()));
        return this;
    }
});

CodeReview.AppView = Backbone.View.extend({
    events: {
        'mouseenter .gutter .line': 'highlightRow',
        'mouseleave .gutter .line': 'unhighlightRow',
        'click .gutter .line': 'showForm',
        'click .comment-form .close': 'hideForm',
        'click .comment-form .send': 'sendForm',
        'click .toolbar .toggle-comments': 'toggleComments',
        'click .toolbar .show-help': 'showHelp'
    },

    initialize: function(options) {
        this.$helpWindow = options.helpWindow;
        this.commentsHidden = false;
        this.comments = options.comments;
        this.iaAuthenticated = options.iaAuthenticated;

        this.listenTo(this.comments, 'add', this.addOne);
        this.listenTo(this.comments, 'reset', this.addAll);
        this.listenTo(this.comments, 'all', this.render);

        this.comments.fetch();
    },

    render: function(){

    },

    showHelp: function(){
        this.$helpWindow.modal('show');
    },

    toggleComments: function(e){
        if (this.commentsHidden){
            this.commentsHidden = false;
            $(e.target).attr('class', 'icon-minus-sign');
            this.$('.comment').show();
            this.$('.comment-placeholder').show();
        } else {
            this.commentsHidden = true;
            $(e.target).attr('class', 'icon-plus-sign');
            this.$('.comment').hide();
            this.$('.comment-placeholder').hide();
        }
    },

    sendForm: function(e){
        var $form = $(e.target).parents('.comment-form');
        var content = $form.find('textarea').val();

        this.comments.create({
            content: content,
            row: $form.data('row-number')
        }, {wait: true});

        $form.find('.close').trigger('click');
    },

    hideForm: function(e){
        var $form = $(e.target).parents('.comment-form');
        var $row = $form.prev();
        this._getGutter($row).next().remove();
        $form.remove();
    },

    showForm: function(e){
        if ( ! this.iaAuthenticated){
            alert('Authenticate please!');
            return;
        }

        var $el = $(e.target);
        if ($el.next().hasClass('form-placeholder')){
            return;
        }
        var $row = this._getRow($el);
        var $form = $(this._renderForm());

        $form.data('row-number', this.getRowNumber($el));

        $row.after($form);
        $el.after('<div class="form-placeholder"></div>');
    },

    highlightRow: function(e){
        var $el = $(e.target);
        $el.addClass('highlighted');
        this._getRow($el).addClass('highlighted');
    },

    unhighlightRow: function(e){
        var $el = $(e.target);
        $el.removeClass('highlighted');
        this._getRow($el).removeClass('highlighted');
    },

    addOne: function(comment){
        var view = new CodeReview.CommentView({
            model: comment
        });
        var row = comment.get('row');
        this._getRow(row).after(view.render().el);

        this._getGutter(comment.get('row')).after(view.getPlaceholder());
    },

    addAll: function(){
        this.comments.each(this.addOne, this);
    },

    getRowNumber: function($el){
        var cls = this._rowIndexCls($el);
        return cls.match(/\d+/g)[0];
    },

    _renderForm: function(){
        return '<div class="comment-form"><textarea></textarea><button class="btn send">Send</button><button type="button" class="close pull-left">&times;</button></div>';
    },

    _getRow: function($gutterEl){
        if (_.isNumber($gutterEl)){
            return this.$('.code .container .line.number'+$gutterEl);
        }
        return this.$('.code .container .line.'+this._rowIndexCls($gutterEl));
    },

    _getGutter: function($row){
        if (_.isNumber($row)){
            return this.$('.gutter .line.number'+$row);
        }
        return this.$('.gutter .line.'+this._rowIndexCls($row));
    },

    _rowIndexCls: function($el){
        return $el.attr('class').match(/number\d+/g)[0];
    }
});
Artem
Artem 6 дней, 13 часов прошло
Ответ | Ссылка

Чем больше и сложнее код, тем меньше шанс, что его кто-то будет смотреть. :(

Priler
Priler 4 дня, 17 часов прошло
Ответ | Ссылка

Ответ на Artem
Чем больше и сложнее код, тем меньше шанс, что его кто-то будет смотреть. :(

Да лень смотреть это, и не написано что делать будет этот код

Priler
Priler 4 дня, 17 часов прошло
Ответ | Ссылка

А блин написано же :) Хорошее приложение но код большой(

alerion
alerion 4 дня, 16 часов прошло
Ответ | Ссылка

Ответ на Artem
Чем больше и сложнее код, тем меньше шанс, что его кто-то будет смотреть. :(

Это для демонстрации просто так закинул. Для заманухи.