Copy Model Object From a Model To Another In Django - django

I have to model. I want to copy model object from a model to another:
Model2 is copy of Model1 (this models has too many m2m fields)
Model1:
class Profile(models.Model):
user = models.OneToOneField(User)
car = models.ManyToManyField(Car)
job = models.ManyToManyField(Job)
.
.
This is a survey application. I want to save user's profile when he/she attends the survey (because he can edit profile after survey)
I have created another model to save user profile when he takes survey (Im not sure its the right way)
class SurveyProfile(models.Model):
user = models.OneToOneField(SurveyUser) #this is another model that takes survey users
car = models.ManyToManyField(Car)
job = models.ManyToManyField(Job)
How can I copy user profile from Profile to SurveyProfile.
Thanks in advance

deepcopy etc won't work because the classes/Models are different.
If you're certain that SurveyProfile has the all of the fields present in Profile*, this should work (not tested it):
for field in instance_of_model_a._meta.fields:
if field.primary_key == True:
continue # don't want to clone the PK
setattr(instance_of_model_b, field.name, getattr(instance_of_model_a, field.name))
instance_of_model_b.save()
* (in which case, I suggest you make an abstract ProfileBase class and inherit that as a concrete class for Profile and SurveyProfile, but that doesn't affect what I've put above)

I'm having a tough time understanding what you wrote above, consequently I'm not 100% certain if this will work, but what I think I would do is something like this, if I'm understanding you right:
class Model2Form(ModelForm):
class Meta:
model = models.Model2
and then
f = Model2Form(**m1.__dict__)
if f.is_valid():
f.save()
But I think this looks more like poor database design then anything, without seeing the entire model1 I can't be certain. But, in any event, I'm not sure why you want to do that anyway, when you can simply use inheritance at the model level, or something else to get the same behavior.

Here's the function I've been using, it builds on model_to_dict. Model_to_dict just returns the ids of foreign keys + not their instances, so for those I replace them with the model itself.
def update_model(src, dest):
"""
Update one model with the content of another.
When it comes to Foreign Keys, they need to be
encoded using models and not the IDs as
returned from model_to_dict.
:param src: Source model instance.
:param dest: Destination model instance.
"""
src_dict = model_to_dict(src, exclude="id")
for k, v in src_dict.iteritems():
if isinstance(v, long):
m = getattr(src, k, None)
if isinstance(m, models.Model):
setattr(dest, k, m)
continue
setattr(dest, k, v)

This is how I do it (note: this is in Python3, you might need to change things - get rid of the dictionary comprehension - if you are using python 2):
def copy_instance_kwargs(src, exclude_pk=True, excludes=[]):
"""
Generate a copy of a model using model_to_dict, then make sure
that all the FK references are actually proper FK instances.
Basically, we return a set of kwargs that may be used to create
a new instance of the same model - or copy from one model
to another.
The resulting dictionary may be used to create a new instance, like so:
src_dict = copy_instance_kwargs(my_instance)
ModelClass(**src_dict).save()
:param src: Instance to copy
:param exclude_pk: Exclude the PK of the model, to ensure new records are copies.
:param excludes: A list of fields to exclude (must be a mutable iterable) from the copy. (date_modified, for example)
"""
# Exclude the PK of the model, since we probably want to make a copy.
if exclude_pk:
excludes.append(src._meta.pk.attname)
src_dict = model_to_dict(src, exclude=excludes)
fks={k: getattr(src, k) for k in src_dict.keys() if
isinstance(getattr(src, k, None), models.Model) }
src_dict.update(fks)
return src_dict

I came across something similar but I needed to check also if ForeignKey fields have compatible models. I end up with the following method:
def copy_object(obj, model):
kwargs = dict()
for field in model._meta.fields:
if hasattr(obj, field.name) and not field.primary_key:
if field.remote_field is not None:
obj_field = obj._meta.get_field(field.name)
if obj_field.remote_field != field.remote_field:
continue
kwargs[field.name] = getattr(obj, field.name)
return model(**kwargs)

So, if I'm interpreting your problem correctly, you have an old model (Profile), and you're trying to replace it with the new model SurveyProfile. Given the circumstances, you may want to consider using a database migration tool like South in the long run. For now, you can run a script in the Django shell (manage.py shell):
from yourappname.models import *
for profile in Profile.objects.all():
survey_profile = SurveyProfile()
# Assuming SurveyUser has user = ForeignKey(User)...
survey_profile.user = SurveyUser.objects.get(user=profile.user)
survey_profile.car = profile.car
survey_profile.job = profile.job
survey_profile.save()
Using South
If this project needs to be maintained and updated in the long term, I would highly recommend using a database migration package like South, which will let you modify fields on a Model, and migrate your database painlessly.
For example, you suggest that your original model had too many ManyToManyFields present. With South, you:
Delete the fields from the model.
Auto-generate a schema migration.
Apply the migration.
This allows you to reuse all of your old code without changing your model names or mucking with the database.

Related

Force a cascading delete for a Django model

I have a Django model with foreign-key relations that are marked as deletion.PROTECT, and I am OK with that behavior, since it's how the model should behave in most scenarios.
However, there is one use case for those models where I kind of need to do a "hard delete" (ie user wants to delete their account). In that case, I'd really like everything to behave like a CASCADE, instead of having to delete each of the foreign-key relationships manually. Is there a way to do this cleanly? In an ideal world, the model.delete() call would take a parameter that is something like force_cascade=True.
As django also creates the database with PROTECTED relations you need to do the cascading deletion yourself manually. The database itself will otherwise forbid the deletion.
Django's ORM can help you with that, the only thing you need to do is to find recursively all references to the user and delete them in reverse order.
It is also an advantage to do this manually as you might want to replace some occurrences of the user with a substitute (i.e. a virtual "deleted user"). I could think of comments in a message board that should be kept even so if the user deletes their account.
To find the relations pointing to the current user and replace them with a ghost user, you can use the following snippet.
from typing import List
from django.contrib.auth import get_user_model
from django.db.models import Model
from django.db.models.fields.reverse_related import (
ManyToOneRel,
ForeignObjectRel,
)
User = get_user_model()
def get_all_relations(model: Model) -> List[ForeignObjectRel]:
"""
Return all Many to One Relation to point to the given model
"""
result: List[ForeignObjectRel] = []
for field in model._meta.get_fields(include_hidden=True):
if isinstance(field, ManyToOneRel):
result.append(field)
return result
def print_updated(name, number):
"""
Simple Debug function
"""
if number > 0:
print(f" Update {number} {name}")
def delete_user_and_replace_with_substitute(user_to_delete: User):
"""
Replace all relations to user with fake replacement user
:param user_to_delete: the user to delete
"""
replacement_user: User = User.objects.get(pk=0) # define your replacement user
# replacement_user: User = User.objects.get(email='email#a.com')
for field in get_all_relations(user_to_delete):
field: ManyToOneRel
target_model: Model = field.related_model
target_field: str = field.remote_field.name
updated: int = target_model.objects.filter(
**{target_field: user_to_delete}
).update(**{target_field: replacement_user})
print_updated(target_model._meta.verbose_name, updated)
user_to_delete.delete()
For a real deletion simply replace the .update(...) function with a .delete() call (don't forget to recursively look for protected relations before, if needed)
There might be also a postgresql related solution that I am not aware of. The given solution is database independent.
In general it is a good idea to keep every relation PROTECTED to prevent accidentally deleting important database entries and delete manually with care.
models.PROTECT is a setting for the table in the database. You would have to issue raw SQL instructions to override it, and that would be database-specific (and I don't have a clue how to do that).
The alternative is to navigate the "tree" of objects that you want to remove, and then delete objects working from the protected "leaves" inwards to the "trunk". So if you had
class Bar( models.Model):
user = models.ForeignKey( User, models.PROTECT, ...)
...
class Foo( models.Model):
bar = models.ForeignKey( Bar, models.PROTECT, ... )
...
Then to delete a user object user you would need
def delete_user( user):
for bar in user.bar_set.all():
bar.foo_set.all().delete()
bar.delete()
user.delete()
I'd wrap it in a transaction so it either deleted everything or nothing.
It will hit the DB multiple times. I'm assuming that the number of related (bar, baz) objects is fairly small and that you won't be deleting users very often.
I have always wondered what one does if instance a has a protected foreign key relation to instance b and vice versa (maybe via intermediates). At face value this means you can create objects that are un-deleteable.

Concise way of getting or creating an object with given field values

Suppose I have:
from django.db import models
class MyContentClass(models.Model):
content = models.TextField()
another_field = models.TextField()
x = MyContentClass(content="Hello, world!", another_field="More Info")
Is there a more concise way to perform the following logic?
existing = MyContentClass.objects.filter(content=x.content, another_field=x.another_field)
if existing:
x = existing[0]
else:
x.save()
# x now points to an object which is saved to the DB,
# either one we've just saved there or one that already existed
# with the same field values we're interested in.
Specifically:
Is there a way to query for both (all) fields without specifying
each one separately?
Is there a better idiom for either getting the old object or saving the new one? Something like get_or_create, but which accepts an object as a parameter?
Assume the code which does the saving is separate from the code which generates the initial MyContentClass instance which we need to compare to. This is typical of a case where you have a function which returns a model object without also saving it.
You could convert x to a dictionary with
x_data = x.__dict__
Then that could be passed into the object's get_or_create method.
MyContentClass.objects.get_or_create(**x_data)
The problem with this is that there are a few fields that will cause this to error out (eg the unique ID, or the _state Django modelstate field). However, if you pop() those out of the dictionary beforehand, then you'd probably be good to go :)
cleaned_dict = remove_unneeded_fields(x_data)
MyContentClass.objects.get_or_create(**cleaned_dict)
def remove_unneeded_fields(x_data):
unneeded_fields = [
'_state',
'id',
# Whatever other fields you don't want the new obj to have
# eg any field marked as 'unique'
]
for field in unneeded_fields:
del x_data[field]
return x_data
EDIT
To avoid issues associated with having to maintain a whitelist/blacklist of fields you, could do something like this:
def remove_unneeded_fields(x_data, MyObjModel):
cleaned_data = {}
for field in MyObjModel._meta.fields:
if not field.unique:
cleaned_data[field.name] = x_data[field.name]
return cleaned_Data
There would probably have to be more validation than simply checking that the field is not unique, but this might offer some flexibility when it comes to minor model field changes.
I would suggest to create a custom manager for those models and add the functions you want to do with the models (like a custom get_or_create function).
https://docs.djangoproject.com/en/1.10/topics/db/managers/#custom-managers
This would be the cleanest way and involves no hacking. :)
You can create specific managers for specific models or create a superclass with functions you want for all models.
If you just want to add a second manager with a different name, beware that it will become the default manager if you don't set the objects manager first (https://docs.djangoproject.com/en/1.10/topics/db/managers/#default-managers)

How to track changes when using update() in Django models

I'm trying to keep track of the changes whenever a field is changed.
I can see the changes in Django Admin History whenever I use the .save() method, but whenever I use the .update() method it does not record whatever I changed in my object.
I want to use update() because it can change multiple fields at the same time. It makes the code cleaner and more efficient (one query, one line...)
Right now I'm using this:
u = Userlist.objects.filter(username=user['username']).update(**user)
I can see all the changes when I do
u = Userlist.objects.get(username=user['username'])
u.lastname=lastname
u.save()
I'm also using django-simple-history to see the changes.setup.
From the docs:
Finally, realize that update() does an update at the SQL level and,
thus, does not call any save() methods on your models, nor does it
emit the pre_save or post_save signals (which are a consequence of
calling Model.save())
update() works at the DB level, so Django admin cannot track changes when updates are applied via .update(...).
If you still want to track the changes on updates, you can use:
for user in Userlist.objects.filter(age__gt=40):
user.lastname = 'new name'
user.save()
This is however more expensive and is not advisable if the only benefit is tracking changes via the admin history.
Here's how I've handled this and it's worked well so far:
# get current model instance to update
instance = UserList.objects.get(username=username)
# use model_to_dict to convert object to dict (imported from django.forms.models import model_to_dict)
obj_dict = model_to_dict(instance)
# create instance of the model with this old data but do not save it
old_instance = UserList(**obj_dict)
# update the model instance (there are multiple ways to do this)
UserList.objects.filter(username=username).update(**user)
# get the updated object
updated_object = UserList.objects.get(id=id)
# get list of fields in the model class
my_model_fields = [field.name for field in cls._meta.get_fields()]
# get list of fields if they are different
differences = list(filter(lambda field: getattr(updated_object, field, None)!= getattr(old_instance, field, None), my_model_fields))
The differences variable will give you the list of fields that are different between the two instances. I also found it helpful to add which model fields I don't want to check for differences (e.g. we know the updated_date will always be changed, so we don't need to keep track of it).
skip_diff_fields = ['updated_date']
my_model_fields = []
for field in cls._meta.get_fields():
if field.name not in skip_diff_fields:
my_model_fields.append(field.name)

Creation of dynamic model fields in django

This is a problem concerning django.
I have a model say "Automobiles". This will have some basic fields like "Color","Vehicle Owner Name", "Vehicle Cost".
I want to provide a form where the user can add extra fields depending on the automobile that he is adding. For example, if the user is adding a "Car", he will extra fields in the form, dynamically at run time, like "Car Milage", "Cal Manufacturer".
Suppose if the user wants to add a "Truck", he will add "Load that can be carried", "Permit" etc.
How do I achieve this in django?
There are two questions here:
How to provide a form where the user can add new fields at run time?
How to add the fields to the database so that it can be retrieved/queried later?
There are a few approaches:
key/value model (easy, well supported)
JSON data in a TextField (easy, flexible, can't search/index easily)
Dynamic model definition (not so easy, many hidden problems)
It sounds like you want the last one, but I'm not sure it's the best for you. Django is very easy to change/update, if system admins want extra fields, just add them for them and use south to migrate. I don't like generic key/value database schemas, the whole point of a powerful framework like Django is that you can easily write and rewrite custom schemas without resorting to generic approaches.
If you must allow site users/administrators to directly define their data, I'm sure others will show you how to do the first two approaches above. The third approach is what you were asking for, and a bit more crazy, I'll show you how to do. I don't recommend using it in almost all cases, but sometimes it's appropriate.
Dynamic models
Once you know what to do, this is relatively straightforward. You'll need:
1 or 2 models to store the names and types of the fields
(optional) An abstract model to define common functionality for your (subclassed) dynamic models
A function to build (or rebuild) the dynamic model when needed
Code to build or update the database tables when fields are added/removed/renamed
1. Storing the model definition
This is up to you. I imagine you'll have a model CustomCarModel and CustomField to let the user/admin define and store the names and types of the fields you want. You don't have to mirror Django fields directly, you can make your own types that the user may understand better.
Use a forms.ModelForm with inline formsets to let the user build their custom class.
2. Abstract model
Again, this is straightforward, just create a base model with the common fields/methods for all your dynamic models. Make this model abstract.
3. Build a dynamic model
Define a function that takes the required information (maybe an instance of your class from #1) and produces a model class. This is a basic example:
from django.db.models.loading import cache
from django.db import models
def get_custom_car_model(car_model_definition):
""" Create a custom (dynamic) model class based on the given definition.
"""
# What's the name of your app?
_app_label = 'myapp'
# you need to come up with a unique table name
_db_table = 'dynamic_car_%d' % car_model_definition.pk
# you need to come up with a unique model name (used in model caching)
_model_name = "DynamicCar%d" % car_model_definition.pk
# Remove any exist model definition from Django's cache
try:
del cache.app_models[_app_label][_model_name.lower()]
except KeyError:
pass
# We'll build the class attributes here
attrs = {}
# Store a link to the definition for convenience
attrs['car_model_definition'] = car_model_definition
# Create the relevant meta information
class Meta:
app_label = _app_label
db_table = _db_table
managed = False
verbose_name = 'Dynamic Car %s' % car_model_definition
verbose_name_plural = 'Dynamic Cars for %s' % car_model_definition
ordering = ('my_field',)
attrs['__module__'] = 'path.to.your.apps.module'
attrs['Meta'] = Meta
# All of that was just getting the class ready, here is the magic
# Build your model by adding django database Field subclasses to the attrs dict
# What this looks like depends on how you store the users's definitions
# For now, I'll just make them all CharFields
for field in car_model_definition.fields.all():
attrs[field.name] = models.CharField(max_length=50, db_index=True)
# Create the new model class
model_class = type(_model_name, (CustomCarModelBase,), attrs)
return model_class
4. Code to update the database tables
The code above will generate a dynamic model for you, but won't create the database tables. I recommend using South for table manipulation. Here are a couple of functions, which you can connect to pre/post-save signals:
import logging
from south.db import db
from django.db import connection
def create_db_table(model_class):
""" Takes a Django model class and create a database table, if necessary.
"""
table_name = model_class._meta.db_table
if (connection.introspection.table_name_converter(table_name)
not in connection.introspection.table_names()):
fields = [(f.name, f) for f in model_class._meta.fields]
db.create_table(table_name, fields)
logging.debug("Creating table '%s'" % table_name)
def add_necessary_db_columns(model_class):
""" Creates new table or relevant columns as necessary based on the model_class.
No columns or data are renamed or removed.
XXX: May need tweaking if db_column != field.name
"""
# Create table if missing
create_db_table(model_class)
# Add field columns if missing
table_name = model_class._meta.db_table
fields = [(f.column, f) for f in model_class._meta.fields]
db_column_names = [row[0] for row in connection.introspection.get_table_description(connection.cursor(), table_name)]
for column_name, field in fields:
if column_name not in db_column_names:
logging.debug("Adding field '%s' to table '%s'" % (column_name, table_name))
db.add_column(table_name, column_name, field)
And there you have it! You can call get_custom_car_model() to deliver a django model, which you can use to do normal django queries:
CarModel = get_custom_car_model(my_definition)
CarModel.objects.all()
Problems
Your models are hidden from Django until the code creating them is run. You can however run get_custom_car_model for every instance of your definitions in the class_prepared signal for your definition model.
ForeignKeys/ManyToManyFields may not work (I haven't tried)
You will want to use Django's model cache so you don't have to run queries and create the model every time you want to use this. I've left this out above for simplicity
You can get your dynamic models into the admin, but you'll need to dynamically create the admin class as well, and register/reregister/unregister appropriately using signals.
Overview
If you're fine with the added complication and problems, enjoy! One it's running, it works exactly as expected thanks to Django and Python's flexibility. You can feed your model into Django's ModelForm to let the user edit their instances, and perform queries using the database's fields directly. If there is anything you don't understand in the above, you're probably best off not taking this approach (I've intentionally not explained what some of the concepts are for beginners). Keep it Simple!
I really don't think many people need this, but I have used it myself, where we had lots of data in the tables and really, really needed to let the users customise the columns, which changed rarely.
Database
Consider your database design once more.
You should think in terms of how those objects that you want to represent relate to each other in the real world and then try to generalize those relations as much as you can, (so instead of saying each truck has a permit, you say each vehicle has an attribute which can be either a permit, load amount or whatever).
So lets try it:
If you say you have a vehicle and each vehicle can have many user specified attributes consider the following models:
class Attribute(models.Model):
type = models.CharField()
value = models.CharField()
class Vehicle(models.Model):
attribute = models.ManyToMany(Attribute)
As noted before, this is a general idea which enables you to add as much attributes to each vehicle as you want.
If you want specific set of attributes to be available to the user you can use choices in the Attribute.type field.
ATTRIBUTE_CHOICES = (
(1, 'Permit'),
(2, 'Manufacturer'),
)
class Attribute(models.Model):
type = models.CharField(max_length=1, choices=ATTRIBUTE_CHOICES)
value = models.CharField()
Now, perhaps you would want each vehicle sort to have it's own set of available attributes. This can be done by adding yet another model and set foreign key relations from both Vehicle and Attribute models to it.
class VehicleType(models.Model):
name = models.CharField()
class Attribute(models.Model):
vehicle_type = models.ForeigngKey(VehicleType)
type = models.CharField()
value = models.CharField()
class Vehicle(models.Model):
vehicle_type = models.ForeigngKey(VehicleType)
attribute = models.ManyToMany(Attribute)
This way you have a clear picture of how each attribute relates to some vehicle.
Forms
Basically, with this database design, you would require two forms for adding objects into the database. Specifically a model form for a vehicle and a model formset for attributes. You could use jQuery to dynamically add more items on the Attribute formset.
Note
You could also separate Attribute class to AttributeType and AttributeValue so you don't have redundant attribute types stored in your database or if you want to limit the attribute choices for the user but keep the ability to add more types with Django admin site.
To be totally cool, you could use autocomplete on your form to suggest existing attribute types to the user.
Hint: learn more about database normalization.
Other solutions
As suggested in the previous answer by Stuart Marsh
On the other hand you could hard code your models for each vehicle type so that each vehicle type is represented by the subclass of the base vehicle and each subclass can have its own specific attributes but that solutions is not very flexible (if you require flexibility).
You could also keep JSON representation of additional object attributes in one database field but I am not sure this would be helpfull when querying attributes.
Here is my simple test in django shell- I just typed in and it seems work fine-
In [25]: attributes = {
"__module__": "lekhoni.models",
"name": models.CharField(max_length=100),
"address": models.CharField(max_length=100),
}
In [26]: Person = type('Person', (models.Model,), attributes)
In [27]: Person
Out[27]: class 'lekhoni.models.Person'
In [28]: p1= Person()
In [29]: p1.name= 'manir'
In [30]: p1.save()
In [31]: Person.objects.a
Person.objects.aggregate Person.objects.all Person.objects.annotate
In [32]: Person.objects.all()
Out[33]: [Person: Person object]
It seems very simple- not sure why it should not be a considered an option- Reflection is very common is other languages like C# or Java- Anyway I am very new to django things-
Are you talking about in a front end interface, or in the Django admin?
You can't create real fields on the fly like that without a lot of work under the hood. Each model and field in Django has an associated table and column in the database. To add new fields usually requires either raw sql, or migrations using South.
From a front end interface, you could create pseudo fields, and store them in a json format in a single model field.
For example, create an other_data text field in the model. Then allow users to create fields, and store them like {'userfield':'userdata','mileage':54}
But I think if you're using a finite class like vehicles, you would create a base model with the basic vehicle characteristics, and then create models that inherits from the base model for each of the vehicle types.
class base_vehicle(models.Model):
color = models.CharField()
owner_name = models.CharField()
cost = models.DecimalField()
class car(base_vehicle):
mileage = models.IntegerField(default=0)
etc

How to update multiple fields of a django model instance?

I'm wondering, what is a standard way of updating multiple fields of an instance of a model in django? ... If I have a model with some fields,
Class foomodel(models.Model):
field1 = models.CharField(max_length=10)
field2 = models.CharField(max_length=10)
field3 = models.CharField(max_length=10)
...
... and I instantiate it with one field given, and then in a separate step I want to provide the rest of the fields, how do I do that by just passing a dictionary or key value params? Possible?
In other words, say I have a dictionary with some data in it that has everything I want to write into an instance of that model. The model instance has been instantiated in a separate step and let's say it hasn't been persisted yet. I can say foo_instance.field1 = my_data_dict['field1'] for each field, but something tells me there should be a way of calling a method on the model instance where I just pass all of the field-value pairs at once and it updates them. Something like foo_instance.update(my_data_dict). I don't see any built-in methods like this, am I missing it or how is this efficiently done?
I have a feeling this is an obvious, RTM kind of question but I just haven't seen it in the docs.
It's tempting to mess with __dict__, but that won't apply to attributes inherited from a parent class.
You can either iterate over the dict to assign to the object:
for (key, value) in my_data_dict.items():
setattr(obj, key, value)
obj.save()
Or you can directly modify it from a queryset (making sure your query set only returns the object you're interested in):
FooModel.objects.filter(whatever="anything").update(**my_data_dict)
You could try this:
obj.__dict__.update(my_data_dict)
It seems like such a natural thing you'd want to do but like you I've not found it in the docs either. The docs do say you should sub-class save() on the model. And that's what I do.
def save(self, **kwargs):
mfields = iter(self._meta.fields)
mods = [(f.attname, kwargs[f.attname]) for f in mfields if f.attname in kwargs]
for fname, fval in mods: setattr(self, fname, fval)
super(MyModel, self).save()
I get primary key's name, use it to filter with Queryset.filter() and update with Queryset.update().
fooinstance = ...
# Find primary key and make a dict for filter
pk_name foomodel._meta.pk.name
filtr = {pk_name: getattr(fooinstance, pk_name)}
# Create a dict attribute to update
updat = {'name': 'foo', 'lastname': 'bar'}
# Apply
foomodel.objects.filter(**filtr).update(**updat)
This allows me to update an instance whatever the primary key.
Update using update()
Discussion.objects.filter(slug=d.slug)
.update(title=form_data['title'],
category=get_object_or_404(Category, pk=form_data['category']),
description=form_data['description'], closed=True)