Psychic IRC Support In 10 Easy Steps
This list is somewhat specific to Django and #django channel on freenode.net.
Psychic support is what people are looking for when they expect others to just know their development environment and their code. If one doesn’t supply enough information on the problem, no matter how good a hacker the person on the other end of the conversation, it becomes a silly guessing game. So the following list is actually about avoiding psychic support and similar situations.
What Not To Ask
- Don’t ask to ask! Just ask your question. Don’t ever say “I have a question about X, can anybody help me?”. Even worse is “Hello! Is anyone there?”.
- Don’t ask for competency. It’s unkind to try testing people whether they are worthy of your question or not. This’ll only decrease your chances to get an answer.
- Don’t ask what’s already documented. This is the most frequent and the most disturbing problem in IRC support channels. Django is exceptionally well documented. Nobody wants to parrot docs just because you’re too lazy to read them.
Psychic Support? You’ll Have To Wait… Long.
- Ideally you should supply related code with your question. But it’s understandable for beginners to not know what to paste. Even so it becomes annoying when the person asking the question has a you-dont-need-to-know-that attitude. If you know so much, why are you asking here?
- Don’t paste in the channel. It’s unreadable, I can’t copy & paste it for testing and worst of all you’re flooding the channel. If it’s more than 1 (in writing one) line, use a pasting service.
- When pasting tracebacks always use the plaintext version. Don’t insist it’s plaintext when you have selected it with mouse from an HTML page and copied. Instead use Switch to copy-and-paste view link right next to Traceback header. Django is civilized.
Hit-N-Run
- This is an unsolved mystery. Enter the chat room, send your question, leave as soon as you see it on screen… If you don’t get an answer immediately, don’t get discouraged. If there’s no activity, your message will be noticed when others check their clients[1]. If there’s activity and your message gets scrolled, you can repeat your question in reasonable intervals[2]. If you don’t have time to wait for and work through answers, you should postpone your question. Better not waste somebody else’s time for nothing.
- IRC support is for practical problems. So if you are asking a question; please be kind enough to try out the proposed solutions and tell us about the results. Asking the same question again and again, even tough somebody has suggested a solution, won’t help you much. Instead try building on the topic, interact with whoever has joined the conversation and give feedback for their answers.
A Winning Effort Begins With Preparation
- Django runs on Python. Python is a dynamic language. Just fire up the Django shell[3] and try out different possibilities. Make this a habit and you’ll see it is much more effective to run a couple of commands than asking someone else to guess their result for you.
- Again, read the relevant docs before you ask. Don’t expect the title of a documentation page to be your exact question. Just read whatever you can find on subject. If people see that you’ve done your homework they will be willing to give you more attention.
- Google your question. If your question is generic enough there is probably a blog post about it somewhere. StackOverflow has quite an impressive repertoire of Django related questions as well. In fact I suggest you to use SO for your non-trivial questions.
[1] | Some of us have work to do you know. ;) |
[2] | Reasonable depends on the number of messages and the number of joins/leaves. In my opinion you should wait 5 minutes at least. |
[3] | Use manage.py shell command to enter the Django shell. |
Serving Static Media In Django Development Server
Important
There is a newer version of this post. Information below is no longer valid!
There is a misconception about how static files (a.k.a media files) are handled in Django. Actually it is quite clearly documented here and here. Nevertheless a question about this comes up in the mailing-list or IRC channel frequently:
- Where do I put my media files?
- Django can’t find my foo.gif!
- How can I link my CSS?
First of all, just to make it clear; just because a server returns a response body with an internal URL doesn’t necessarily mean it will be available on that server. It is one thing that your templates produce the correct URL to a media file and another thing that your server actually serves that resource on that URL. Django development server doesn’t automagically serve media files.
Settings
There are three settings to get right: MEDIA_ROOT, MEDIA_URL and ADMIN_MEDIA_PREFIX. MEDIA_ROOT is the absolute filesystem path where your media files are. I usually set it like:
MEDIA_ROOT = os.path.join(
os.path.abspath(os.path.dirname(__file__)),
'media',
)
This will set MEDIA_ROOT to point to the media directory in your project directory. MEDIA_URL and ADMIN_MEDIA_PREFIX are URL’s:
MEDIA_URL = '/media/'
ADMIN_MEDIA_PREFIX = '/media/admin/'
With this setup, to serve admin media in production, all I need to do is to symlink media folder of admin app into my media directory. Of course you can set MEDIA_URL to point to another domain/subdomain. Such as http://media.mydomain.com/. But this way you can’t serve your media from development server.
URL Configuration
Add the following code snipplet at the end of your root urls.py
if settings.DEBUG:
from django.views.static import serve
_media_url = settings.MEDIA_URL
if _media_url.startswith('/'):
_media_url = _media_url[1:]
urlpatterns += patterns('',
(r'^%s(?P<path>.*)$' % _media_url,
serve,
{'document_root': settings.MEDIA_ROOT}))
del(_media_url, serve)
settings.DEBUG == True doesn’t necessarily mean development server is running. But it is a good indicator since deploying with development server is not a good idea for many reasons. Notice here we don’t serve media unless MEDIA_URL is an absolute URL on our server.
Templates
Finally we need to specify media URL’s correctly. To avoid hard-coding media path we will be using {{ MEDIA_URL }} context variable in our templates. To have {{ MEDIA_URL }} included automatically in each template we need to do two things:
- Make sure you have django.core.context_processors.media in your TEMPLATE_CONTEXT_PROCESSORS.
- Make sure each view is using a RequestContext.
Afterwards all we need to do is to specify our media URL’s like this:
<img src="{{ MEDIA_URL }}img/header.jpeg" />
This will be translated to:
<img src="/media/img/header.jpeg" />
Bonus
While we are at it, why not serve our 500 and 404 pages statically. When DEBUG == True, 500 (server error) and 404 (not found) situations are handled with special debugging views. So there’s no chance to test your error pages. Add the following code, just like static serving code:
if settings.DEBUG:
urlpatterns += patterns('',
(r'^404/',
'django.views.generic.simple.' \
'direct_to_template',
{'template': '404.html'}),
(r'^500/',
'django.views.generic.simple.' \
'direct_to_template',
{'template': '500.html'}))
Now when you visit /500/ and /404/ on your development server you will be served a fake error page.
Django Permission System
Permission system that comes with django.contrib.auth allows you to create and assign permissions for your models. Permissions can be assigned to users or groups. A user has permissions directly assigned to her as well as permissions inherited from her groups. Permission scope is always model classess (as opposed to individual model instances). That means if you are just checking FooModel.can_do_bar permission on a user; that user can do bar on all FooModel instances.
This built-in permission system is simple; as in you shouldn’t expect complex authorization schemes to be implemented easily. It is mainly used by django.contrib.admin and you can use it without any hassle for your very simple authorization requirements. Beauty of this permission system is it’s simplicity. Here are a few strong points:
- All your models get add, change and delete permissions created automatically.1 These three are all you need for most of your models.
- Admin uses permissions internally. But more importantly permissions can be granted/revoked dynamically via admin.
- Simple design encourages you to keep authorization scheme clean.2
The last point is very important; permissions shouldn’t leak into business logic. If you have a project that goes beyond a CMS, you probably have business objects. Business objects might have quite complex states and typically interact with each other in more than straightforward ways. On the other hand models in CMS style projects have simple states that are independent (of other models). Take a blog post; it is either published or not and its author is simply a ForeignKey to Users. If you depend heavily upon permissions for complex state transitions of your models you will soon find yourself in a dead end.
Permissions in Fixtures
Tip
There is a --natural argument to dumpdata command since Django 1.2, see documentation here. (Thanks Joseph)
One problem, and it’s a big problem, I’ve had with permissions is that you can’t store permissions in fixtures. Here is what happens:
- Relationships are stored in fixtures as primary keys.
- Permission are created by the framework programmatically in a specific order.
- When you add/delete a new model or add/delete a custom permission this order changes.
- Therefore primary keys of permissions change.
- As a result permission data in your fixture are invalid.
I’m a big fan of fixtures. More often than not you have some models that will be used as definitions (they’re partially fixed data) or you want to kickstart your project with placeholder data. It’s quite easy in Django. After creating your models, launch admin and create your data. Then simply dumpdata, maybe post-process a little bit and you have your initial_data! Not so easy if you have permissions in that basket.
Best solution I can come up with now is to hook a post_syncdb function and add your permissions programmatically.
Authorization
Basic usage of permissions is to check if a user has a certain Permission and branch accordingly:
@login_required
def some_view(request):
user = request.user
if user.has_perm('foo.bar_baz'):
# go on and bar that baz
else:
# display an error message
# ...rest of the view
When you need to combine conditions, it is a good idea to abstract authorization check in a function:
class Foo(models.Model):
# stuff that goes into a model
def can_bar(self, user):
return user.has_perm('app.bar_foo') and user.baz >= self.baz
You can combine this with techniques explained in here and here to achieve column level permissions.
Row level permissions are cooking as well. There is a branch for per object permissions. You can checkout this branch from subversion with the following command:
svn co http://code.djangoproject.com/svn/django/branches/per-object-permissions
Generic Permissions
Let’s define permissions problem in a more generic way. A permission determines if:
- An actor can
- Perform an action
- On an object
- Depending on arbitrary number of runtime conditions [optional]
In our little implementation actors will be auth.User instances and objects, naturally, models. Actions will simply be keys of string type. Because of the possibility that our permission can have conditions, it should be a callable. Then we will have all the power Python/Django has.
Let’s place our permissions on our models:
class FooPermissions(PermissionMixin):
def __warg(self):
return max(self.quux - self.parent.quux, 1)
@staticmethod
def allows_bar_for(actor):
return actor.has_perm('app.bar_foo') and \
actor.status == 'barrable'
def allows_baz_for(self, actor):
if self.qux_set.count() > 10:
return True
else:
return self.__warg() > 5 or actor.is_staff
class Foo(models.Model, FooPermissions):
# fields, managers, etc...
First we created a permissions mixin, subclassing a PermissionMixin I’ll show it to you in a minute, and mixed it with our model definition. This way we keep permissions seperate at the source level. Also, for instance __warg method will not be accessible from our model. You can do anything inside your permissions class, you can even have side effects if you like. all you have to do is to follow a naming convention for your permission methods. Here is the PermissionMixin class:
from django.contrib.auth.models import User
class PermissionError(StandardError):
pass
class PermissionMixin(object):
def attempt(self, action, actor, msg=None):
return PermissionMixin._attempt(self, action, actor, msg=None)
@classmethod
def cls_attempt(cls, action, actor, msg=None):
return PermissionMixin._attempt(cls, action, actor, msg=None)
@staticmethod
def _attempt(obj, action, actor, msg):
if actor.__class__ != User or not isinstance(action, basestring):
raise TypeError
if getattr(obj, 'allows_%s_for' % action.lower().replace(' ',
'_'))(actor):
return True
else:
if msg is None:
msg = u'%s doesn\'t have permission to %s %s' % (actor.username,
action.lower(),
repr(obj))
raise PermissionError(msg)
When you want to check for a permission you just need it’s key (name of the action) and have your User at hand:
# Check a permission on a model:
Foo.cls_attempt('bar', user)
# Check a permission on an instance:
foo.attempt('baz', user)
Notice that we are still using Permissions from auth. Our PermissionMixin can do complex logic, but its hard-coded. We still need something like Permissions for dynamic behaviour. Why not use what’s built-in?
You can find PermissionMixin’s code at Django snipplets.
Conclusion
Before employing a similar mechanism, you should think hard if you absolutely positively need it. Maybe you can simplify your UI? Maybe there is a nice tradeoff between code complexity and interactivity of your application? Maybe you can move this logic into model.save() or form.save() or form.clean()? Or maybe your code will be best organized if you use PermissionMixins.
I just wanted to show that you can build on auth’s Permissions. Just don’t get discouraged when you find out you can’t do x out of the box. Be it permissions or some model magic or making something available in your templates; Django is Python, it can be extended easily.
1: Provided you have auth app installed of course.
2: Ask yourself; “Is this really a must?”, and not only for permission related complications.
Using Layouts In Qooxdoo - Part 5: Basic & Canvas
This is the fifth part of a tutorial series about layout managers, container objects and object hierarchies in Qooxdoo. Read the first part here.
Basic Layout Manager
qx.ui.layout.Basic layout manager positions your widgets with absolute coordinates. It doesn’t have much to offer except the freedom of manual positioning.
We supply a left and top property when we add our widgets. Following example places buttons in a circular path, like a clock.
/*
#asset(qooxdoolayouts/*)
*/
qx.Class.define("qooxdoolayouts.Application",
{
extend: qx.application.Standalone,
members: {
main: function(){
this.base(arguments);
var main_container = new qx.ui.container.Composite();
var layout_manager = new qx.ui.layout.Basic();
main_container.setLayout(layout_manager);
var btn_size = 50;
var distance = 175;
var pi2 = 2 * 3.141592;
center = {
x: Math.round(qx.bom.Viewport.getWidth()/2)-btn_size/2,
y: Math.round(qx.bom.Viewport.getHeight()/2)-btn_size/2
}
for (var i=0; i<12; i++) {
var button = new qx.ui.form.Button("CW"+(i+1).toString());
button.setWidth(btn_size);
button.setHeight(btn_size);
var dist_x = Math.round(distance * Math.sin(-(i/12+0.5)*pi2));
var dist_y = Math.round(distance * Math.cos(-(i/12+0.5)*pi2));
main_container.add(button, {
left: center.x + dist_x,
top: center.y + dist_y
});
}
var application_root = this.getRoot();
application_root.add(main_container);
}
}
});
Nevermind the math. What is important is; this would be much harder to achieve with other, more advanced managers such as Grid.
Canvas Layout Manager
qx.ui.layout.Canvas is a slightly advanced version of Basic. Canvas lets you define margins (of all four directions) and dimensions in percentage as well as in pixels.
Canvas allows you to define all four margins. You don’t have to define all of them of course. For example if you define a top and bottom margin remaining space will be your widget’s height. Alternatively you can specify a bottom margin and a height for you widget. This way it will keep its position relative to container’s bottom edge.
One thing to keep in mind is margins are defined against container, not against a bounding box like DOM. Say, if you set the same left on two widgets, they’ll be stacked on top of each other if they happen to have the same vertical coordinates. The following example mimics a Grid layout using Canvas:
/*
#asset(qooxdoolayouts/*)
*/
qx.Class.define("qooxdoolayouts.Application",
{
extend: qx.application.Standalone,
members: {
main: function(){
this.base(arguments);
var main_container = new qx.ui.container.Composite();
var layout_manager = new qx.ui.layout.Canvas();
main_container.setLayout(layout_manager);
main_container.add(new qx.ui.form.Button("Child Widget 1"), {
left: 20,
top: 10
});
main_container.add(new qx.ui.form.Button("Child Widget 2"), {
left: 140,
right: 20,
top: 10
});
main_container.add(new qx.ui.form.Button("Child Widget 3"), {
left: 20,
top: 50,
bottom: 10
});
main_container.add(new qx.ui.form.Button("Child Widget 4"), {
left: 140,
right: 20,
top: 50,
bottom: 10
});
main_container.getChildren()[0].setWidth(100);
main_container.getChildren()[2].setWidth(100);
var application_root = this.getRoot();
application_root.add(main_container);
application_root.addListener("resize", function(e) {
main_container.set({
"width": qx.bom.Viewport.getWidth(),
"height": qx.bom.Viewport.getHeight(),
});
}, this);
}
}
});
We set two widgets’ widths manually (see lines 37 & 38) to get all the margins (almost) equal. The first row only has top margin set, but the second row has both top and bottom margins. We have given widgets in the second row top margins taking:
- Top margin of first row
- Height of the widgets in the first row
and
- Margin between first and second rows
into account. The same thing is true for the left margin of second column. This is because, as I mentioned, margins are calculated relative to container edges.
If you need to position your controls relative to one another, like DOM positioning, you should probably look into other layout managers like Grid. A good example of a use case for Canvas can be a mapping application such as Google Maps.
As I mentioned earlier Canvas layout accepts parameters in percentages. Following example sets vertical margins in pixels but horizontal margins in percentages:
/*
#asset(qooxdoolayouts/*)
*/
qx.Class.define("qooxdoolayouts.Application",
{
extend: qx.application.Standalone,
members: {
main: function(){
this.base(arguments);
var main_container = new qx.ui.container.Composite();
var layout_manager = new qx.ui.layout.Canvas();
main_container.setLayout(layout_manager);
var button = new qx.ui.form.Button("Child Widget 1");
button.setMinWidth(250);
button.setMaxWidth(400);
main_container.add(button, {
left: "30%",
right: "30%",
top: 40,
bottom: 40
});
var application_root = this.getRoot();
application_root.add(main_container);
application_root.addListener("resize", function(e) {
main_container.set({
"width": qx.bom.Viewport.getWidth(),
"height": qx.bom.Viewport.getHeight(),
});
}, this);
}
}
});
You can find a list of posts in this series here
1: Whether your widget grows this high depends. Let’s say that height is reserved for your widget.
Using Layouts In Qooxdoo - Index
Using Layouts In Qooxdoo tutorial series is finished. I tried my best to explain how layout managers work and how they differ from each other. This tutorial is for those who have little or no object oriented GUI programming experience. Especially JavaScript/DOM programmers learning Qooxdoo. I hope it is helpful.
Complete list of parts: