How to rename a django app withouth affecting the database? - django

In an old project there was an app (let's call it oldapp). I am maintaining this project and I have to rename the app to something else (let's say newapp). The problem is that database tables have been created with the oldapp_ prefix.
I want to avoid to re-creating the database with new prefixes, for obvious reasons.
So, is this possible to rename the app without disturbing the project?
UPDATE: This is one of the errors I am getting:
relation "newapp_dma" does not exist

You can, but I wouldn't advise it. For consistency, migrate your data to the newapp name (possibly using South, or simply do some manual renaming in the database).
If you must, have a look at the db_table in your Meta subclass inside your app classes:
class Something(Model):
class Meta:
db_table = 'newapp_something'
See the docs.
But, this will cause confusion at some point, using inconsistent names.

Related

Django: keep log after deleting a foreignkey

My question is simple:
I have a Folder model, and I have a Log model. Here are the simplified examples:
class Folder(models.Model):
name = models.CharField(max_length=100)
class Log(models.Model):
folder = models.ForeignKey(Folder, on_delete="I NEED HELP HERE")
What this means is, A folder is created by a User, and each action done on the folder, is logged by using the Log model (user-edited folder, the user did this with folder, etc...)
What I want to do is simple: if some admin deletes the folder completely, I want to still keep the name of the folder. All actions in the on_delete attribute, do not let me do that.
Any ideas?
PS: those are not the real models, just an illustration of what I want to do. The real ones are 100+ lines of code :P
You can implement your own on_delete trigger, since the on_delete just expects a callable that will be called if the object to which it refers is deleted: Django does triggering management itself. But that will likely only make it more complicated.
I think it might be better to make Folder a "soft-deleted" model. Here in case you delete a model, it is not deleted from the database, but the database has a column with a boolean that specifies if the record is "deleted". If it is deleted, it will normally not be included in standard querysets on the model, but there are still ways to obtain the item to which it refers. Furthermore you can later recover a deleted object by "undeleting" it. You can for example make use of the django-softdelete package [GitHub].
I think I found an answer, all I did was to set a default value, then put on_delete=models.SET_DEFAULT.
The default value is a variable returned from a function, declared right above the model, taking the username and storing it in a different table.

How to replace a foreignkey with another using a migration

I'd like to replace an existing ForeignKey pointing at my User model with one pointing at a profile model.
The change in the model is:
created_by=models.ForeignKey(settings.AUTH_USER_MODULE)
To:
created_by=models.ForeignKey(settings.PROFILE_MODEL)
The auto-generated migration looks like (with constants subbed in):
migrations.AlterField(
model_name=MODEL,
name='created_by',
field=models.ForeignKey(to=settings.PROFILE_MODEL),
preserve_default=True,
),
I also have ManyToManyFields to deal with as well. What I have in my head is I'd like a function to run on each MODEL object to resolve the user object to the profile object. How would I go about doing this?
The relationship between user and profile is (and vice versa):
User.profile = Profile
Edit: Forgot to mention, if the auto-generated migration is run you get the following error:
ValueError: Lookup failed for model referenced by field
APP1.MODEL.created_by: APP2.PROFILE_MODEL
As I understand now you want to migrate only our app without expecting anything to be changed to the global auth User model. Then it's easy. Migrations work nice with symbolic settings names.
I tried it with Django 1.7. It is possible to switch between settings.AUTH_USER_MODEL and settings.PROFILE_MODEL back and forth without any problem. A migration can be created and applied after every change. The tested model had also a ManyToManyField and mutual relationships between User and Profile.
I see you have APP1 and APP2. Maybe you make migrations for both and they are circular dependent so that a part of other application migration should be applied before the current one migration can be completely applied and vice versa. It can be simplified by spliting a change to more smaller and making automatic migrations after every change so that they are less dependent. A OneToOneField is better than two mutual foreign keys and its reverse relation is even so useful. A foreign key can be changed temporarily to IntegerField(null=True) in the worst case in order to simplify data migration. It is really viable more or less nice.
The question looked nice initially, but the problem should be better specified to be reproducible.
Edited by removing the original text after reading information in comments:

Django (South): change model application

I have defined some models within an application, call it "blog".
djangoproject
/blog
models.py
I now want to change the models location, for example put them here:
djangoproject
/blog
xxx
/all_models
models.py
From the code point of view, this is pretty trivial, but the I guess there will be problems on the database since all the tables Django and South created are now called blog_posts blog_comments, Django relies on this naming convention and I don't want to lose the data already present in the database.
How to do this?
The easiest thing is not to bother changing the tables at all, but the code. Inside the Meta class of each of your models, put the declaration db_table = "blog_tablename", and Django will find them without problems.
You can solve this in two ways.
The first and easier one is to provide a db_table in Meta class of each of your models. The other is create a migration to apply the change.
As far as I know south doesn't support table rename, so you should do it as a three way migration:
Move de models, and create migration (now you have both tables old and new)
Create a data migration and iterate over the former table, copying objects to later
Remove the former model, and create a migration for it.
You can read a little bit more about the second way in south docs

How do I make changes to a model in Django?

How does Django handle changes to my Model? Or, what help does it offer me to do this?
I am thinking of a situation where I have already have published data to my DB which I don't want to lose, but I need to make changes to my data model - for example, adding extra fields to a particular class, changing the types of fields, etc. My understanding is that syncdb won't ever alter tables that already exist in the DB.
For example, let's say I have the following model:
class Person(models.Model):
name = models.CharField(max_length=200)
phone_number=models.CharField(max_length=200)
hair_colour=CharField(max_length=50)
Things I might want to do to Person off the top of my head:
I wish to add an 'age' field.
I realise I want to use IntegerField instead of CharField for phone_number (whether this is a good idea or not, is out of scope...) - assuming it's possible.
I realise that I no longer wish to define hair_colour 'inline' within Person, because several people share the same hair colour - I wish instead to change this to be a foreign key to some other model.
Whilst I can imagine some of these are tough/impossible for the framework to 'guess' exactly what needs to be done to my data if all I do is update the models.py, I can imagine that there might still be some tooling to help enable it - does it exist?
In particular I imagine there must be some good patterns for option 1.
I'm very new to Django and have no experience with any other ORM-type stuff, which I think this is - I've always been a bit suspicious of ORMs, mainly for the reasons above :)
Django itself will not attempt to modify an already created database table. What you are trying to do is typically called "Migration" and there are a couple of different Database Migration tools available for Django.
South
Schema Migrations
Data Migrations
Backwards Migrations
Nash Vegas
Schema Migrations
Data Migrations
Django Evolution
Schema Migrations
Data Migrations (Unknown)
Backwards Migrations (Unknown)
Of the three South is probably the most widely used but they each have different ways of dealing with migrations. You can see more details on the comparison on Django Packages.
Much of what you're asking about can be done with the django project South. You add it as an INSTALLED_APP. Create a baseline, then as your model changes it creates SQL statements to convert your tables and the rows with-in the tables to the new model format.

Django: How to dynamically add tag field to third party apps without touching app's source code

Scenario: large project with many third party apps. Want to add tagging to those apps without having to modify the apps' source.
My first thought was to first specify a list of models in settings.py (like ['appname.modelname',], and call django-tagging's register function on each of them. The register function adds a TagField and a custom manager to the specified model. The problem with that approach is that the function needs to run BEFORE the DB schema is generated.
I tried running the register function directly in settings.py, but I need django.db.models.get_model to get the actual model reference from only a string, and I can't seem to import that from settings.py - no matter what I try I get an ImportError. The tagging.register function imports OK however.
So I changed tactics and wrote a custom management command in an otherwise empty app. The problem there is that the only signal which hooks into syncdb is post_syncdb which is useless to me since it fires after the DB schema has been generated.
The only other approach I can think of at the moment is to generate and run a 'south' like database schema migration. This seems more like a hack than a solution.
This seems like it should be a pretty common need, but I haven't been able to find a clean solution.
So my question is: Is it possible to dynamically add fields to a model BEFORE the schema is generated, but more specifically, is it possible to add tagging to a third party model without editing it's source.
To clarify, I know it is possible to create and store Tags without having a TagField on the model, but there is a major flaw in that approach in that it is difficult to simultaneously create and tag a new model.
From the docs:
You don't have to register your models
in order to use them with the tagging
application - many of the features
added by registration are just
convenience wrappers around the
tagging API provided by the Tag and
TaggedItem models and their managers,
as documented further below.
Take a look at the API documentation and the examples that follow for how you can add tags to any arbitrary object in the system.
http://api.rst2a.com/1.0/rst2/html?uri=http://django-tagging.googlecode.com/svn/trunk/docs/overview.txt#tags
Updated
#views.py
def tag_model_view(request, model_id):
instance_to_tag = SomeModel.objects.get(pk=model_id)
setattr(instance_to_tag, 'tags_for_instance', request.POST['tags'])
...
instance_to_tag.save()
...returns response
#models.py
#this is the post_save signal receiver
def tagging_post_save_handler(sender, instance, created):
if hasattr(instance, 'tags_for_instance'):
Tag.objects.update_tags(instance, instance.tags_for_instance)