I'm looking for functionality vaguely like that provided by Semantic MediaWiki. In short, I'd like for a user, in an arbitrary text field, to be able to do things like the following (I'm making up the markup as I go).
*Hi, everyone, don't forget that we have [[::AfricanSwallow.count]] African Swallows in our land.
*Did you know that Harry the European Swallow has carried [[::EuropeanSwallow.get(name="harry").coconuts.count]] coconuts back with him?
In addition to these kinds of features, I'd like to be able to autocomplete inline - perhaps when the user starts typing.
I can do all of these things, but I'm hoping that some or all of them have been done. Any idea if that's the case?
I think something like this is feasible but making it universal (allowing full read-only access to the ORM) would be very difficult to make in a secure way.
Here are some ideas:
Limit the actions to a predefined set of explicitly marked methods on a custom manager class. For example:
from django.db import models
class MarkupAccessManager(models.Manager):
def count(self):
return super(MarkupAccessManager, self).count()
count.expose_to_markup = True
class AfricanSwallow(models.Model):
objects = MarkupAccessManager()
To refer to models from the markup, you could take advantage of the django.contrib.contenttypes framework and the tags could have the following format: app_label.model_name action or app_label.model_name action arg1 arg2.
Depending on the markup language you choose, you could either use custom tags (if the language provides them), Django template tags, or plain regular expressions. Once you get the contents of a tag, this is how you could replace it with the output of the referred method:
from django.contrib.contenttypes.models import ContentType
def replace_tag(tag):
"""
'birds.africanswallow count' => birds.models.AfricanSwallow.objects.count()
"""
bits = tag.split()
model_ref = bits[0]
action = bits[1]
args = bits[2:]
try:
ct = ContentType.objects.get_by_natural_key(*model_ref.split('.'))
except ContentType.DoesNotExist:
return 'Invalid model reference.'
model = ct.model_class()
method = getattr(model._base_manager, action, None)
if not method or not method.expose_to_markup:
return 'Invalid action.'
return method(*args)
To provide autocomplete, something along these lines would help you to build a list of all the available options:
from django.db.models.loading import get_models
from django.contrib.contenttypes.models import ContentType
def model_refs():
for model in get_models():
if isinstance(model._base_manager, MarkupAccessManager):
ct = ContentType.objects.get_for_model(model)
yield '%s.%s' % (ct.app_label, ct.model)
def actions():
for attr_name in dir(MarkupAccessManager):
attr = getattr(MarkupAccessManager, attr_name)
if attr.expose_to_markup:
yield attr.__name__
I haven't tested the code. Hope this helps a bit.
Most elegant solution would be to create a compiler that will allow execution of only certian instructions. Find out more # http://en.wikibooks.org/wiki/Compiler_Construction
Another way is to use exec() but you should avoid this as it brings a lot of security issues into your application. You can always try to parse the string first (for valid syntax) but it will still be a possible vulnerability.
Related
I want to use hypothesis to test a tool we've written to create avro schema from Django models. Writing tests for a single model is simple enough using the django extra:
from avro.io import AvroTypeException
from hypothesis import given
from hypothesis.extra.django.models import models as hypothetical
from my_code import models
#given(hypothetical(models.Foo))
def test_amodel_schema(self, amodel):
"""Test a model through avro_utils.AvroSchema"""
# Get the already-created schema for the current model:
schema = (s for m, s in SCHEMA if m == amodel.model_name)
for schemata in schema:
error = None
try:
schemata.add_django_object(amodel)
except AvroTypeException as error:
pass
assert error is None
...but if I were to write tests for every model that can be avro-schema-ified they would be exactly the same except for the argument to the given decorator. I can get all the models I'm interested in testing with ContentTypeCache.list_models() that returns a dictionary of schema_name: model (yes, I know, it's not a list). But how can I generate code like
for schema_name, model in ContentTypeCache.list_models().items():
#given(hypothetical(model))
def test_this_schema(self, amodel):
# Same logic as above
I've considered basically dynamically generating each test method and directly attaching it to globals, but that sounds awfully hard to understand later. How can I write the same basic parameter tests for different django models with the least confusing dynamic programming possible?
You could write it as a single test using one_of:
import hypothesis.strategies as st
#given(one_of([hypothetical(model) for model in ContentTypeCache.list_models().values()]))
def test_this_schema(self, amodel):
# Same logic as above
You might want to up the number of tests run in this case using something like #settings(max_examples=settings.default.max_examples * len(ContentTypeCache.list_models())) so that it runs the same number of examples as N tests.
I would usually solve this kind of problem by parametrising the test, and drawing from the strategy internally:
#pytest.mark.parametrize('model_type', list(ContentTypeCache.list_models().values()))
#given(data=st.data())
def test_amodel_schema(self, model_type, data):
amodel = data.draw(hypothetical(model_type))
...
What is/are the best practices to use get_model() and when should it be imported ?
Ref: https://docs.djangoproject.com/en/1.8/ref/applications/
You usually use get_model() when you need to dynamically get a model class.
A practical example: when writing a RunPython operation for a migration, you get the app registry as one of the args, and you use apps.get_model('TheModel') to import historical models.
Another example: you have an app which has dynamically built serializers and you set their Meta.model to the class you just got with get_model() .
Yet another example is importing models in AppConfig.ready() with self.get_model().
An important thing to remember, if you are using AppConfig.get_model() or apps.get_models(), that they can be used only once the application registry is fully populated.
The other option (from .models import TheModel) is just the default way to import models anywhere in your code.
These are just examples though, there are many other possible scenarios.
I Prefer, use .models import, cause is a simple way to get the Model Object.
But if you works with metaclasses, maybe the get_model, would be the best option.
def get_model(self, app_label, model_name=None):
"""
Returns the model matching the given app_label and model_name.
As a shortcut, this function also accepts a single argument in the
form <app_label>.<model_name>.
model_name is case-insensitive.
Raises LookupError if no application exists with this label, or no
model exists with this name in the application. Raises ValueError if
called with a single argument that doesn't contain exactly one dot.
"""
self.check_models_ready()
if model_name is None:
app_label, model_name = app_label.split('.')
return self.get_app_config(app_label).get_model(model_name.lower())
Maybe this SO POST, can help too.
Example:
class MyModel(models.Model):
field1=models.CharField(..)
field2=models.DateTimeField()
def today(self):
return self.field2
When I look at this in the admin site, field2 is formatted differently than the today field.
How can I tell the admin site to treat today like it's treating field2? I.e., tell Django admin that 'today' is a models.DateTimeField?
Here is what it's showing:
Field2 today
April 5, 2011, 9:10 a.m. 2011-04-11 08:47:27
To obtain DateTime object call datetime.datetime.now() instead of datetime.datetime.today()
EDIT:
Or use models.DateField() for field2 instead of models.DateTimeField() :-)
EDIT2:
Here is the solution:
def today(self):
from django.utils import formats
return formats.localize(self.field2)
That's some really really weird behaviour. At a total guess, it may have something to do with django settings; specifically the DATETIME_FORMAT (and related) settings. The framework probably does introspection on fields, and if they are of DateTime type, are rendered according to the aforementioned settings.
Introspection on methods wouldn't make sense in the majority of cases, so I could understand this behaviour if it is the case.
Try modifying the settings accordingly (provide different datetime formats), and see if the fields change and the method remains the same.
Edit:
Looking at django.contrib.databrowse.datastructures, there is a section of code that does something like:
if isinstance(self.field, models.DateTimeField):
objs = capfirst(formats.date_format(self.raw_value, 'DATETIME_FORMAT'))
I'd imagine a similar thing happening within the admin app, though I can't find an exact reference at the moment.
To achieve what you want, you'll need to format your datetime appropriately:
def today(self):
from django.conf import settings
return self.field2.strftime(settings.DATETIME_FORMAT)
Or, using #cata's comment:
def today(self):
from django.utils.formats import localize
return localize(self.field2)
If you choose to supply a "list_display" item through your own function, and you're not happy with the default output, you'll need to format it yourself. In this case, if you want to have identical formatting to what the DateTime database field ends up with:
from django.utils import formats
def today(self):
return formats.localize(self.field2)
Background:
templates/admin/change_list.html
uses the template tag
django.contrib.admin.templatetags.admin_list.result_list
which in turn will call
django.contrib.admin.templatetags.admin_list.items_for_result()
to render the individual column values for each row.
You'll see that both your values start off being a DateTime instance, either through database lookup or calling your function, see
django.contrib.admin.util.lookup_field()
but the return value "f" will only be a field if there was a database field. You provided a function, so lookup_field() will only provide the value, and "f" will be None.
So in items_for_result(), your value will run through the "if f is None" block and miss out on
result_repr = display_for_field(value, f)
In other words,
django.contrib.admin.util.display_for_field()
will only be called on the database value to format according to the field type, so this is the treatment your function value is missing out on:
elif isinstance(field, models.DateField) or isinstance(field, models.TimeField):
return formats.localize(value)
and you'll need to do that last line yourself, as shown above.
EDIT: Regarding your question
How can I tell the admin site to treat
today like it's treating field2? I.e.,
tell Django admin that 'today' is a
models.DateTimeField?
It's not a models.DateTimeField, it's a function value. If it were a models.DateTimeField, it would be describing your model. Look at all the stuff that entails: http://docs.djangoproject.com/en/dev/ref/models/fields/
In your example, you really could just use field2. Apparently you want to do things to its value, calculate it etc. - so what's today.db_column then?
That said, it would be nice if function values that are DateTime instances were run through format.localize() by default, as that's what the documentation on localization seems to be promising.
By the way, I would rather define a formatted value in the ModelAdmin than in the model itself. I usually call it something like "formatted_today" (to keep the datetime value of the original today()), it's just that if the Admin is the only place that needs the formatted value, imho that's where it should be defined.
All previous answers provide solutions, that will handle timezone info incorrectly in new Django versions.
field2 and today will generally show different time if settings.USE_TZ==True.
I found this question today and have spent some time to figure out the correct way:
from django.db import models
from django.contrib import admin
from django.utils.timezone import template_localtime
from django.utils.formats import localize
class MyModel(models.Model):
# ...
field2=models.DateTimeField()
class MyModelAdmin(admin.ModelAdmin):
# ...
def today(self, obj):
return localize(template_localtime(obj.field2))
admin.site.register(MyModel, MyModelAdmin)
I read today that Django 1.3 alpha is shipping, and the most touted new feature is the introduction of class-based views.
I've read the relevant documentation, but I find difficult to see the big advantage™ that I could get by using them, so I'm asking here for some help in understanding them.
Let's take an advanced example from the documentation.
urls.py
from books.views import PublisherBookListView
urlpatterns = patterns('',
(r'^books/(\w+)/$', PublisherBookListView.as_view()),
)
views.py
from django.shortcuts import get_object_or_404
from django.views.generic import ListView
from books.models import Book, Publisher
class PublisherBookListView(ListView):
context_object_name = "book_list"
template_name = "books/books_by_publisher.html",
def get_queryset(self):
self.publisher = get_object_or_404(Publisher, name__iexact=self.args[0])
return Book.objects.filter(publisher=self.publisher)
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(PublisherBookListView, self).get_context_data(**kwargs)
# Add in the publisher
context['publisher'] = self.publisher
return context
And now let's compare it to a “plain-old-views” solution, made by myself in 5 minutes for this question (I apologize for any error you may find in it).
urls.py
urlpatterns = patterns('books.views',
url(r'^books/(\w+)/$', 'publisher_books_list', name="publisher_books_list"),
)
views.py
from django.shortcuts import get_object_or_404
from books.models import Book, Publisher
def publisher_books_list(request, publisher_name):
publisher = get_object_or_404(Publisher, name__iexact=publisher_name)
book_list = Book.objects.filter(publisher=publisher)
return render_to_response('books/books_by_publisher.html', {
"book_list": book_list,
"publisher": publisher,
}, context_instance=RequestContext(request))
The second version to me looks:
Equivalent in functionality
A lot more readable (self.args[0]? awful!)
Shorter
Not less DRY-compliant
Is there something big I'm missing? Why should I use them? Are those on the documentation? If so then what would be the ideal use case? Are mixins that useful?
Thanks in advance to anybody who contributes!
P.S. for those who might wonder, I was never enthralled by generic views as well: as soon as I needed some advanced functionality, they became no shorter than regular views.
You can subclass a class and refine methods like get_context_data for specific cases, and leave the rest as-is. You can't do that with functions.
For instance, you might need to create a new view that does everything a previous one does, but you need to include extra variable in the context. Subclass the original view and override the get_context_data method.
Also, separating the steps needed to render the template into separate methods promotes clearer code - the less done in a method, the easier it is to understand. With regular view functions, it's all dumped into the one processing unit.
If self.args[0] is bothering you, the alternative is:
urlpatterns = patterns('books.views',
url(r'^books/(?P<slug>\w+)/$', 'publisher_books_list', name="publisher_books_list"),
)
Then you could use self.kwargs['slug'] instead, making it slightly more readable.
Your example function and class are not equal in features.
The class based version provide pagination for free and forbid the use of other HTTP verbs than GET.
If you want to add this to your function, it's going to be much longer.
But it is, indeed, more complicated.
This is the first I'm hearing of this -- and I like it.
The advantage I see here, honestly, is that it makes views more consistent with Django overall. Models are classes and I've always felt that views should be too. I know not everything is but views and models are the two heavily used types.
As for the technical advantage? Well, in Python everything is a class (or object?) -- so is there really a difference? Isn't it 99% syntactical sugar in the first place?
One way to think about class based views, is that they are like a the Django admin with training wheels off and therefore a lot more flexible (but more difficult to understand).
For example the list-display in the admin is clearly based on the generic ListView. The simplest list view you would only define a model or queryset.
class MyExampleView(ListView);
model = ExampleModel
You will need to supply your own template, but it will basically be the same as the most basic ModelAdmin. The list_display attribute in the model admin will tell it what fields to display, whereas in the ListView you would do this in the template.
class SpeciesAdmin(admin.ModelAdmin):
list_display = ['name']
admin.site.register(ExampleModel , ExampleModelAdmin)
With the admin you have a parameter
list_per_page = 100
which defines how many objects per page. List view has
paginate_by = 100
which achieves the same thing. Likewise if you look into customising the admin heavily, you will see a lot of overlap.
This site here should give you a better idea of what they do as well.
http://ccbv.co.uk/
I have a multiple ModelForm classes that each represent a different Model. I would like to have a generic 'create' function that loads the specified model form based on a URL parameter. It is possible to load a model dynamically with this:
model_name = 'TestModel'
m = get_model('AppLabel', model_name)
Does anyone know how I can achieve the same for ModelForms, something like:
modelform_name = 'TestModelForm'
f = get_form('AppLabel', modelform_name)
if f.is_valid():
...
I can not think of a way to do this with generic views - they require the ModelForm to be passed, rather than just its name. If I get the model with get_model then pass that to the generic view it will display a form but I am unable to exclude model fields.
TIA for any tips
When you create a ModelForm it does not register itself with its model's app. (Based on experience and a quick browse through the source).
Here are some otheroptions I can think of:
All ModelForm classes exist in a single module: Use getattr on that module based on the string.
ModelForm's are spread out among many models and you have a reasonable (<30) amount of forms:
Create a dictionary mapping from form strings you expect to ModelForm classes. For example:
from some_app.forms import FirstModelForm
from another_app.forms import SecondModelForm
from additional_app.forms import FirstModelForm as AdditionalAppFirstModelForm # Will allow for managing conflicting names easily.
form_mapping = {
'FirstModelForm': FirstModelForm,
'SecondModelForm': SecondForm,
'AdditionalAppFirstModelForm': AdditionalAppFirstModelForm,
}
request_form_class = request.POST.get('form_class')
f = form_mapping.get(request_form_class)(request.POST)
if f.is_valid():
f.save()
You're dealing with a lot of forms: Create a baseclass for your ModelForm, or replace the BaseModelFormMetaclass at runtime. You'll have to deal with issues such as name conflicts, duplicate ModelForms for the same Model "automagically", so prepare for some headaches. It would be pretty rad if you could pull it off.
Personally (as you can probably see), I'd just go with option #2.
An alternate method for this is to replace forms.py with a package called forms. Then, in __init__.py within that package, import all your ModelForms.
Then you can use sdolan's Option #1.