Django CMS copy model instance issue - django

I have some issue with Django CMS 3.0 plugin model copy_relations function.
Example from documentation
def copy_relations(self, oldinstance):
for associated_item in oldinstance.associated_item.all():
# instance.pk = None; instance.pk.save() is the slightly odd but
# standard Django way of copying a saved model instance
associated_item.pk = None
associated_item.plugin = self
associated_item.save()
is not working and CMS is only modifying instance of plugin model. In this example I need to add associated_item.id = None for it to work.
This is not my first Django CMS project, but is first in 3.0. In previous versions I did follow documentation and everything was ok, but not this time.
The question is: is the documentation not up-to-date or I did something wrong on the way when creating models?
edit:
Here is where I found how to make Django CMS to save a copy of model instance:
Due to how inheritance works, you have to set both ``pk`` and ``id`` to None
I need to do this for all models with copy_relations, and I want to know why is this required.

Obviously in your case associated_item points to a model which inherits from another model. There are two types of model inheritance in Django: abstract and multi-table.
In the case of abstract inheritance, the base model is abstract, i.e. it is not in the database and just serves as a template for the models which inherit from it.
In the case of multi-table inheritance, all of the models are in DB, with submodels linked to the base model via a ForeignKey.
For regular models .pk and .id are one and the same thing (you can even check it with my_model.pk is my_model.id), while for multi-table submodels .id is the id field of the base model, and .pk is the ForeignKey used to connect the submodel to the base model. In fact, the numeric value of .pk and .id is still the same (because .pk points to .id), but nonetheless they are two separate columns in the DB. In my opinion this Django design decision is somewhat inconsistent, but that's what we have.
That's why you need to set both fields to None for Django to lose track of the model and see it as a new model instance.

Related

how to create django multiple user types

I am working on a project where I need to have 3 types of Users.
Admin
Vendor
Customer
My first approach to this problem was to define all models by sub-classing the AbstractUser model
class Customer(AbstractUser):
pass
class Vendor(AbstractUser):
pass
so the question is do i need to craete a UsermManager for every usertype model ?
As I said into a comment, the best way to achieve what you want is to use the Multi Table Inheritance paradigm in Django. It defines OneToOne relations underneath so you could do it on your own.
Extending the User model is also explained HERE (official Django documentation) and it is done the way I told you (with OneToOne relations).

A Django admin interface for my Django admin interface

My Django project has models ShopType and ShopItem. For each ShopType object I create, I want to associate — not a single ShopItem object — but a subclass of ShopItem. Basically, I want a model type in Django that is not a TextField or an ImageField or the like, but a ModelField.
Example: Using the Django admin interface, I create a new instance of ShopType called CheeseShop. This in turn creates a new Model called Cheese which inherits all of its behavior from ShopItem. If I go to the main page of my Django admin site, there's now a new link called "Cheeses", and I can create a bunch of Cheeses as I please.
Of course, I suspect that's not actually possible: If I create — say — PetShop, I wouldn't just have to add a new record to the database, I'd have to augment the database schema itself to include Pets (along with registering the new Pet Model with the admin interface and probably some other complicated things I haven't thought of yet).
But maybe there's an existing Django solution for this pattern? Or maybe there's a completely different, better-suited approach to the problem that might achieve the same effect?
Any thoughts?

How can one best mark a model field as required in Django?

In Django, I would like the ability to mark certain model fields as required at the model (or at least database) level, to make sure that I am specifying them explicitly (i.e. not relying on defaults) when creating objects.
Currently, Django lets you designate a model field as required at the forms level (by specifying blank=False in the model). However, it doesn't seem like there is a similar option to get this behavior at the model or database level.
It does seem like there are various hacks to achieve something similar though. For example, you can set the default to something that violates a database constraint. For example, you can do things like:
models.CharField(_('characters'), max_length=4, default=None)
or
models.CharField(_('characters'), max_length=4, default="abcdef")
The former example works when saving to the database since None violates the default not-null constraint of null=False (raising an IntegrityError). The latter works because a DataError is raised when saving. But I don't know if this is guaranteed to work across all databases, etc.
Am I missing something, or is there a better way?
If django models called full_clean() automatically on save(), your check would run at the model level without a form. I've been playing with making this the default behavior in my django projects by creating an auto-clean model subclass which does full_clean() on save(), then deriving my models off that.
If you want to learn why it isn't already like this: Why doesn't django's model.save() call full_clean()?

Django/South: Migrating different classes to common base class

I have several classes, e.g. Band, Album, Song. Each has the fields title (which is sometimes called name instead) and slug, as well as other, class-specific fields.
I'd like to use South in order to convert them into children of a common base class, Article, which would contain every object's title and slug. However, I'm not sure how South's data migrations are supposed to work in my case; can you help?
You just need to think about the changes South will make to your models, and plan for that.
If Article is an abstract class, South will for the most part disregard it. That is to say all the fields will look as if they are fields directly on the model, pretty much the way you have it now. So, the only changes that will occur are where one model was using name before, it will now have a new title field as well. For this reason, you should leave the name field attached through the schemamigration, then create a datamigration to move its value to the new title field, and finally remove it in another schemamigration.
If Article is a standard base class (multiple-table inheritance), then all your models will get a new OneToOneField to Article. Again, leave all the fields in tact on your models though the schemamigration, then create a datamigration where you will run through each and create an Article instance using the old data on the model and assign that Article instance to the one-to-one field. Then, you can remove the old fields in another schemamigration.

adding django admin many-to-many widget

In the django admin documentation, it says the following:
By default, admin widgets for many-to-many relations will be displayed on whichever model contains the actual reference to the ManyToManyField.
Is there a way to get a similar widget to appear on the admin page for the other model, the one where the relationship isn't defined?
There's a couple different ways to get the effect that you're after.
Here's one way, which will get you a similar (but not identical) effect, and probably requires the least coding. (Examples will use classes A and B, assuming that the former has the many to many relationship explicitly defined)
The quickest way: you can use an InlineModelAdmin object:
class AInline(admin.TabularInline):
model = A
class BAdmin(admin.ModelAdmin):
inlines = (AInline,)
admin.site.register(B, BAdmin)
If you want the exact effect of getting a <select multiple>, the way you can do it is to use a custom Form class, and assign it to BAdmin.form.