Выделите её мышкой и нажмите Enter
Документация на русском языке для Django стала реальностью. Благодаря новым возможностям движка Sphinx мы можем оперативно дополнять перевод, по мере обновления оригинальной документации.
Хотим добавить ачивки на сайте. Они уже как бы есть, но нет красивых иконок для отображения в профиле и на сайте. Если кто может быстренько и без напряга сделать с десяток, мы были бы благодарны. Обсуждение на форуме.
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() }
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()
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">×</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
Чем больше и сложнее код, тем меньше шанс, что его кто-то будет смотреть. :(
Это для демонстрации просто так закинул. Для заманухи.