Выделите её мышкой и нажмите Enter
| на поддержку перевода |
|
|
ЯМ:41001223475816
Документация на русском языке для 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
Чем больше и сложнее код, тем меньше шанс, что его кто-то будет смотреть. :(
Это для демонстрации просто так закинул. Для заманухи.