Django reversion does not save revisions made in shell - django

I did the initial installation steps and created the initial revisions, but then when I save a model in django shell, the new revision is not created:
In [1]: s = Shop.objects.all()[0]
In [2]: import reversion
In [3]: s.name = 'a'
In [4]: s.save()
In [5]: s.name = 'b'
In [6]: s.save()
In [7]: reversion.get_for_object(s)
Out[7]: [<Version: <1> "X">]
This is the initial revision.
When I update the model from a view, a revision is created successfully.
What am I missing?
The models.py file is:
...
class Shop(Model):
...
import reversion
reversion.register(Shop)
<EOF>
I see a reversion method among post_save receiver, although it isn't called when I debug it.
I have Django v1.4.1, reversion v1.6.2.

I wrote django-reversion, so I think I can shed some light on this issue.
A Version of a model is automatically saved when a model is saved, providing the following are true:
The model is registered with django-reversion.
The code block is marked up as being within a revision.
Point 1 can be achieved by either registering a model with VersionAdmin, or explicitly calling reversion.register() in your models.py file.
Point 2 can be achieved by using RevisionMiddleware, or the reversion.create_revision() decorator or context manager. Any admin views in VersionAdmin also save a revision.
So, if your shell is not creating Versions, then either point 1 or point 2 is not being met. Here's how to fix it:
If you're using VersionAdmin, import the relevant admin module in your shell code to kick in the auto-registration. Alternatively, call reversion.register() in your models.py file.
In your shell code, using the reversion.create_revision() context manager around your call to save.
with reversion.create_revision():
s.save()
More about this sort of thing on the Low Level API wiki page:
http://django-reversion.readthedocs.org/en/latest/api.html

Related

Use model from the versioned app registry globally

I'd like to run a custom command in my migration, that calls functions from other modules. These functions use some models, and as expected I ran into schema version mismatch (OperationalError: (1054, "Unknown column 'foo' in 'bar'").
If I were to use those models in the custom command I'd access the model with apps.get_model('my_app', 'bar'), but as those models are used in the external functions, I can't do that.
I'm sure, someone ran into this before although I couldn't find anything.
I was thinking about using the unittest.mock.patch decorator but it doesn't feel like the right solution.
I'm wondering if there's a more general solution for this?
The versioned app registries are not globally accessible. You could pass the model as a parameter to the function, and use the current model as the default:
from my_app.models import Bar
def my_function(..., bar_model=Bar):
# Use bar_model instead of Bar
# Your RunPython function
def migrate_something(apps, schema_editor):
my_function(bar_model=apps.get_model('my_app', 'bar'))
You don't have to pass the bar_model parameter if you call it from regular code, but when calling it from a migration you can pass the historical model.
If you need multiple models you could pass apps instead:
from django.apps import apps as global_apps
def my_function(..., apps=global_apps):
Bar = apps.get_model('my_app', 'bar')

get_model() vs from .models import somemodelname

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.

Data migrations in Django

I am working on a data migration for a Django app to populate
the main table in the db with data that will form the mainstay of
the app - this is persistent/permanent data that may added to but
never deleted.
My reference is the Django 1.7 documentation and in particular an
example on page
https://docs.djangoproject.com/en/1.7/ref/migration-operations/#django.db.migrations.operations.RunPython
with a custom method called forward_funcs:
def forwards_func(apps, schema_editor):
# We get the model from the versioned app registry;
# if we directly import it, it'll be the wrong version
Country = apps.get_model("myapp", "Country")
db_alias = schema_editor.connection.alias
Country.objects.using(db_alias).bulk_create([
Country(name="USA", code="us"),
Country(name="France", code="fr"),])
I am assuming the argument to bulk_create is a list of Country model objects not namedtuple objects, although the format looks exactly the same. Is this the case, and could someone please explain what db_alias is?
Also, if I wish to change or remove existing entries in a table using a data migration what are the methods corresponding to bulk_create to do this?
Thanks in advance for any help.
Country is just the same as you would do from app.models import Country. Only thing different, the import always gives you the latest model and apps.get_model in a migration gives you the model at the time of the migration. It continues to edit the model within the initial migration.
About bulk_create; its argument is indeed a list of unsaved Country objects and uses it to do an huge insert into your db. More information about bulk_create can be found here; https://docs.djangoproject.com/en/1.7/ref/models/querysets/#bulk-create.
About db_alias, it is the name of the database you set within your settings. Most of the time it is default, so you can leave it in your code if you just use one database. The function will probably will called more than once if you have more databases set within your settings. More info about using; https://docs.djangoproject.com/en/1.7/ref/models/querysets/#using.
An bulk delete is actually quite simple, you just filter your Countries and call delete on the queryset. So something like;
Country.objects.filter(continent="Europe").delete()
About the persistent/permanent data question, I don't really have a solution for that one. One thing you can do, I think, is overwrite the .delete() function on the model and Manager.

Subclassed ForeignKey field not created for model

I've been looking at Marty Alchin's Apress book 'ProDjango' and I've run into an issue with the last chapter (chapter 11). I'm a relative noob at Python and Django, and although I've tried searching around I can't put my finger on something similar through this and other forums. I have seen Is Pro Django book still relevant? on this site but doesn't answer any specific questions.
The problem revolves around the trying to create a mechanism to track changes - a history of additions, changes and deletions. The first step is creating a user field on models you want to track. In the example project he has created a specialised ForeignKey field hard-coded to relate to Django's built-in 'User' model:
from django.db import models
from django.contrib.auth.models import User
class CurrentUserField(models.ForeignKey):
def __init__(self, **kwargs):
super(CurrentUserField, self).__init__(User, null=True, **kwargs)
There is also a 'contrib_to_class() method later. The class is in a seperate models.py file from the models to which is to be applied.
The use, as I understand, is to add a new field referencing this new field class in your model:
e.g.
class SimpleModel(models.Model):
a_user = CurrentUserField()
But the problem is that when I syncdb the field is nowhere to be found, a 'FieldError' is the usual result trying to access it.
There are many other elements in the book's solution that I haven't tried to copy here, but this is the first and fundamental part.
I'm guessng that changes in Django and/or Python itself are responsible here. Has anyone any pointers?
Thanks.
EDIT: Given the class below in registration.py which is the current_user folder. This is also where you find the models.py holding the CurrentUserField class. The InformationRequest models has:
user = CurrentUserField()
as its last field. All imports are present and appear correct.
class FieldRegistry(object):
_registry = {}
def add_field(self, model, field):
reg = self.__class__._registry.setdefault(model, [])
reg.append(field)
def get_fields(self, model):
return self.__class__._registry.get(model, [])
def __contains__(self, model):
return model in self.__class__._registry
In [1]: from current_user.registration import FieldRegistry
In [2]: from inforequest.models import InformationRequest
In [3]: registry = FieldRegistry()
In [4]: registry.add_field(InformationRequest, InformationRequest._meta.get_field('user'))
---------------------------------------------------------------------------
FieldDoesNotExist Traceback (most recent call last)
<ipython-input-4-6fcbbdcae066> in <module>()
----> 1 registry.add_field(InformationRequest, InformationRequest._meta.get_field('user'))
/usr/local/lib/python2.7/dist-packages/django/db/models/options.pyc in get_field(self, name, many_to_many)
353 if f.name == name:
354 return f
--> 355 raise FieldDoesNotExist('%s has no field named %r' % (self.object_name, name))
356
357 def get_field_by_name(self, name):
FieldDoesNotExist: InformationRequest has no field named 'user'
Trying to look at the admin generates "Unknown field(s)..."
I think I've found an answer to my question: django-simple-history (https://django-simple-history.readthedocs.org/en/latest/), inasmuch it provides part of solution I was trying to achieve and apparently it was built on Marty Alchin's code in Pro Django.

Django Database querying differences

I am going through the creation of the Polls app again, in the Django Docs. I wanted to ask again about one particular thing they do in the django database. The code is shown below:
>>> from polls.models import Poll, Choice
# Make sure our __unicode__() addition worked.
>>> Poll.objects.all()
[<Poll: What's up?>]
# Django provides a rich database lookup API that's entirely driven by
# keyword arguments.
>>> Poll.objects.filter(id=1)
[<Poll: What's up?>]
>>> Poll.objects.filter(question__startswith='What')
[<Poll: What's up?>]
# Get the poll that was published this year.
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Poll.objects.get(pub_date__year=current_year)
<Poll: What's up?>
# Request an ID that doesn't exist, this will raise an exception.
>>> Poll.objects.get(id=2)
Traceback (most recent call last):
...
DoesNotExist: Poll matching query does not exist. Lookup parameters were {'id': 2}
# Lookup by a primary key is the most common case, so Django provides a
# shortcut for primary-key exact lookups.
# The following is identical to Poll.objects.get(id=1).
>>> Poll.objects.get(pk=1)
<Poll: What's up?>
# Make sure our custom method worked.
>>> p = Poll.objects.get(pk=1)
>>> p.was_published_recently()
True
# Give the Poll a couple of Choices. The create call constructs a new
# Choice object, does the INSERT statement, adds the choice to the set
# of available choices and returns the new Choice object. Django creates
# a set to hold the "other side" of a ForeignKey relation
# (e.g. a poll's choices) which can be accessed via the API.
>>> p = Poll.objects.get(pk=1)
# Display any choices from the related object set -- none so far.
>>> p.choice_set.all()
[]
# Create three choices.
>>> p.choice_set.create(choice_text='Not much', votes=0)
<Choice: Not much>
>>> p.choice_set.create(choice_text='The sky', votes=0)
<Choice: The sky>
>>> c = p.choice_set.create(choice_text='Just hacking again', votes=0)
# Choice objects have API access to their related Poll objects.
>>> c.poll
<Poll: What's up?>
If you take a look at the variable c, you will see that it is created using this, p.choice_set.create(choice_text='Just hacking again', votes=0). Now if you created it instead with this: c = p.choice_set.filter(id=3), and when you type in c.poll, it will give you an error. Why does this happen? The console gives me this error : AttributeError: 'Poll' object has no attribute 'create', but I do not understand what it means.
Also, is there any way of getting c.poll to give you an output without having to create a new choice?
--
Thanks in advance
c = p.choice_set.filter(id=3) won't return a single choice object. It returns a queryset composed of a single choice object because, obviously, there is just one object with the same id. Querysets are iterables, which means that if you want to obtain the choice object from that variable it should be: c = p.choice_set.filter(id=3)[0]
That is the difference with choice_set.create: create returns the single created object.
Now, that's not the way to do it. When you know you are querying for a single object, use get.
c = p.choice_set.get(id=3).
See querying documentation for further details.