How to create models directly for OneToOne Relationships in Django - django

As far as I read, I can create Items in the Django Shell and in the admin panel after some configuration.
But what I want is a "Subitem" of an "Item" that is created directly every time I create an "Item".
How it is:
Item is created via admin, needs "Upvote" and "Downvote" subitems to be created too manually.
How do I change Django to directly create Upvote and Downvote for me?
Thank you!

Instead of adding and creating subitems each time an Item is created, I just added the "Downvotes" as an IntegerField.

You just have to mention it when you are creating the model.
Suppose you have a Item model like this -
class Item(models.Model):
item_title = models.CharField(blank=True, null=True)
and then If you want one to one field then the code will be like
class SubItem(models.Model):
item = models.OneToOneField(Item, on_delete=models.CASCADE)
It will create subitem when you create Item. To access it you can directly access with dot notation. Suppose you have an SubItem object sub_item_obj then corresponding sub item is sub_item_obj.item

Related

User model with static set of related models

Say I have a User model in django and I want to add some achievements to users. So I've created an Achieve model:
class Achive:
type = ....
value = ....
status = BooleanField(default=False)
I want all those achieves be a static set of models for every user (20 instances, for example) with ability to delete old and create new achieves. The problem is how to do it. Expected flow is:
1) user granted to use achievement system;
2) user got all those achieves (in admin panel shows like a table);
3) in admin panel per user I can change status of every achieve (affects only on edited user);
4) if new Achieve instance is created — add to all users who have achievements;
5) if existed Achieve instance has been deleted — remove from all users;
Solutions with I came up:
1) use Achieve model with jsonfield. store achieves in json like dictionary, use custom widget for admin panel to show checkboxes to change status). But where to store global set of achievements to create new/delete old ones? how to manage it?
2) use many to many field to Achieve and Achieve model without status. Why: if relation between User ← → Achieve exists, that means that user earn an achieve.
Both solutions I don't really like so hope for your advice.
P.S. sqlite is used as db and not allowed to use another (like mongo, etc.)
Thanks in advance!
What you want is a ManyToMany relationship between Achieve and User, but with the ability to store extra data on the relationship (the status for example).
With a normal ManyToManyField on a Model, Django actually creates an intermediate model to store the relationships in the database. By adding a through argument to your ManyToManyField, you can specify the intermediate model used for the relationship and store extra data with the relationship, as documented here:
class Goal(models.Model):
type = ...
value = ...
achievers = models.ManyToManyField(to=User, through='Achievement', related_name='goals')
class Achievement(models.Model):
status = models.BooleanField()
date_reached = models.DateField(null=True)
goal = models.ForeignKey(to=Goal, on_delete=models.CASCADE)
achiever = models.ForeignKey(to=User, on_delete=models.CASCADE)
then you can create and query the relationships like this, assuming you have a user and a goal:
achievement = Achievement.objects.create(status=True, date_reached=date(2018, 10, 12), achiever=user, goal=goal)
user.goals.filter(achievement__status=True) # gives the achieved goals of a user
goal.achievers.filter(achievement__status=True) # gives the users that achieved a goal

How to create foreign key linked fields in forms just like in Django Admin?

In django admin, you can add, edit, and even delete objects from another model if there is a relationship between the two.
For instance, if my code looks like this:
class Category(models.Model):
...
class Product(models.Model):
...
category = models.ForeignKey(Category)
When I am editing/adding a product using the django admin site, in the category field, I have 3 buttons to add/edit/delete categories. Adding one takes to a new window, and once I submit the form, the category is added, the window is closed, and I am returned to my product form with the extra category present. Like this:
How can I do this in my normal application (outside the admin) using forms?
If I understand your question correctly, you could do what django admin does, which is to link the add button to this:
/admin/<your_app>/<your_model>/add/?_to_field=id&_popup=1
and then it uses a bit of javascript to get back the new object you just created. If you look into the contrib/admin/static/admin/js/admin/RelatedObjectLookups.js file (in django's code), you'll see a few functions that pass the id of the calling field to the popup (in showRelatedObjectPopup), and then bring back the selected id (in dismissRelatedLookupPopup).
This is for adding a new object, but you can look into the logic for changing/deleting.
You can replicate that logic with your own forms.

Is it possible to duplicate a django-mptt model?

Let's say I have a django-mptt model that looks like this:
class Category(MPTTModel):
name = models.CharField(max_length=50)
parent = TreeForeignKey('self', null=True, blank=True, related_name='children', db_index=True)
These Categories (and sub-categories) will serve as a kind of template for the categories used in a project. When a user starts a new project, the user will select which Categores, Sub-Categories, etc. to use. The user should be able to also add/edit Categories. The thing is that they need to be specific the project so that when another project is created, the user will start with the original/default categories.
Is there any way to duplicate the MPTTModel/database table(s) to create project specific one where categories can be edited/added without it affecting the default ones?
I can think of one way to solve this issue which would be to add something like
projects = models.ManyToManyField(Project)
and create a default/template project.
What's the best approach here?
You have the right idea having a "projects" field to match a category with various projects. You might be better off using a "project" field with a ForeignKey relationship:
project = models.ForeignKey(project)
Then you could duplicate the Category, creating a new database row, for each new project. That way if someone working on project foo changes the Category bar it doesn't affect project baz which could also be using Category bar. Both Category bar entries will have different primary keys in the database.
You could also have a project called "default", All the "default" Categories can get duplicated whenever a user creates a new project.
Of course you'll need code to display only Categories from the project your user is working on and to duplicate the Categories when a new project is created.

django manytoone other side

I have a django webapp. I have something like this set up:
class Doc(models.Model)
invoice = models.ForeignKey(Invoice, null=True, on_delete=models.SET_NULL, blank=True)
class Invoice(models.Model)
#bla bla bla
Can I somehow create a form element that would stand for invoice.doc_set.all() ? I want it to be I want it to be a multiple select element. I know I dont want Inline formsets. What I want to see in django admin on Invoice side --> simple Multiple select element with all the instances that can be selected. When selected and saved the particular invoice will become the foreign key instance to them...
You could use a ModelMultipleChoiceField.
https://docs.djangoproject.com/en/dev/ref/forms/fields/#modelmultiplechoicefield
But I don't think that the admin can manage this type of functionality out of the box. You will probably need to extends the admin form and set the FK by yourself.

Replace Django model object with another

I'm working on an application where we have to review model changes before accepting them.
For this, when a user edits one of the company model objects in the app frontend, the form's initial data is filled with data from the original object and then stored into a new object that is flagged using the origin_company field (which is a foreign key to the previous version of the object).
So basically when someone edits a company, a new, inactive company is created.
To apply the changes, I want to copy all data from the changed company to the original company. (I'm doing this with custom django-admin actions.) The easiest way would probably be to start a transaction, delete the original company and change the primary key of the changed company to match the original company. But by doing that, the changed company is not UPDATEd, it is copied. And related database entries (FKs) are not updated either.
Is there an easy way to copy or move all data (including FK relations) from one model instance to another one? Or is my entire way of solving this problem messed up?
Here's the model definition:
class Company(models.Model):
company_name = models.CharField(max_length=150)
...
origin_company = models.ForeignKey('self', related_name=u'CompanyEdits',
null=True, blank=True, default=None)
Try the following:
# get the company instance's data as a dict
data = company_instance.__dict__.copy()
# remove the `id` and `origin_company` keys. don't need these
del data['id']
del data['origin_company_id']
# update the origin company instance
Company.objects.filter(id=company_instance.origin_company.id).update(**data)