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.
Related
I suddenly am having a problem with my django admin panel.
I have the following models:
class Role(models.Model):
name = models.CharField("Name", max_length=32)
class Person(models.Model):
name = models.CharField("Name", max_length=64)
role = models.ForeignKey(Role)
class Team(models.Model):
rep = models.ManyToManyField(
Person,
related_name="rep_on",
limit_choices_to={'role__name': 'Sales Rep'})
eng = models.ManyToManyField(
Person,
related_name="eng_on",
limit_choices_to={'role_id__name': "Engineer"})
(The two options in the limit_choices_to dictionaries are the two methods I've tried. In the past it seemed like role_id__name worked, but now it doesn't.)
If I create roles for the Sales Rep and Engineer, and create a bunch of people and attach roles, when I create a Team in the admin, I get dropdowns like I expect:
There are two problems. Each dropdown contains every Person object, so limit_choices_to doesn't seem to work at all, and of course these relationships are generic "TEAM-PERSON RELATIONSHIPS" and I'd like to be able to at least label those correctly.
I swear this worked before, and now it's not working. Any ideas as to what could be causing this?
EDIT:
I created a toy application to explore this and tried to slowly recreate the full issue. In my real app I am using two Inlines in the admin interface for the Team object and excluding the model fields. I added them into my test code and managed to recreate the issue.
class RepInline(admin.TabularInline):
model = Team.rep.through
class EngInline(admin.TabularInline):
model = Team.eng.through
class TeamAdmin(admin.ModelAdmin):
inlines = [RepInline, EngInline]
admin.site.register(Role)
admin.site.register(Person)
admin.site.register(Team, TeamAdmin)
And my admin screen looks like:
The given HTML Select fields are filtered, but the dropdowns are not, So the issue lies in the TabularInline, so I have to decide if I want to keep them or not.
Im Wondering how exactly does CASCADE work with ManyToMany Fields in Django.
A short example:
class Project(Model):
name = TextField(null=False)
class Job(Model):
projects = ManyToManyField(Project, on_delete=CASCADE, null=False)
name = TextField(null=False)
As you can see I have a ManyToManyField here. So basically a Project can have mutiple Jobs and a Job can belong to Multiple different Projects. What I want is that a Job is automatically deleted only when all Projects it belongs to are deleted. Does CASCADE work like that in this scenario?
CASCADE doesn't work like that. In your case, with CASCADE, with two projects A and B, a job J_DAILY linked to both projects, if you delete project A, then J_DAILY will be deleted too.
If you want your job to "live to its last project". You should change your on_delete to DO_NOTHING and add a check on deleting projects.
#receiver(pre_delete, sender=Project)
def delete_related_jobs(sender, instance, **kwargs):
for job in instance.job_set.all():
# No remaining projects
if not job.projects.exclude(id=instance.id).count():
job.delete()
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
Having a lot of models, I have started to factor out common blocks with the aim of my database reaching second normal form. As the application should be used by a sales team, most entries are some kind of orders. An excerpt from my models file looks like this:
class Order(models.Model):
dl = models.CharField(max_length=100)
cl = models.CharField(max_length=100)
(...)
class Setup(models.Model):
order = models.ForeignKey(Order) # could be OneToOneField()
name = models.CharField(max_length=200)
package = models.CharField(choices=(
('S', 'Small'),
('M', 'Medium'),
('L', 'Large'),
('XL', 'Extra large'),
('C', 'Custom')
), max_length=2)
server = models.ForeignKey(Webserver)
(...)
While it does not make any logical sense to keep the order details out of the model for a setup order, it helps to keep the project maintainable, since setup orders are not the only things coming in and this way you can change the order model and all other models/orders are updated too.
Here comes the problem. For attributes like Setup.server Django's default behaviour of creating a dropdown with all web servers the company can offer is totally fine. If the team decides to add another one, it can simply create another server option on another page. But for order, I would like that the OrderForm is included on the same page as the SetupForm, ideally as a separate Fieldset. After submitting the form, the new order should be added to the database and Setup.order is to be filled. I know that I can code it for this special case, but the application will contain numerous forms, so a generic solution would be better.
A possible solution could be to create a custom models.ForeignKey or models.OneToOneField with a custom option and a generic view that than renders a template with two forms and links the objects afterwards.
Did anyone have a similar problem? Does anyone know a simple, maybe even builtin solution to that?
EDIT 1
I have thought about inline formsets which were the solution to a similar question which used this example
class Contact(models.Model):
...
class Communication(models.Model):
contact = models.ForeignKey(Contact)
and by using a communication fieldset. However, this treats the class containing as the child, which is not the case in my example. But it still is an option, even if it would still have to be automated so it can be used quickly for all links between the other models.
In (a toy version of) my project, there are Owners who own any number of Objects. My models.py file looks like
class Owner(models.Model)
name = models.CharField(max_length=50)
date_of_birth = models.DateField()
class Object(models.Model)
name = models.CharField(max_length=50)
price = models.models.DecimalField(max_digits=9, decimal_places=2)
owner = models.ForeignKey(Owner)
My question relates to the change page for an Owner on the admin site, e.g.
http://mysite.com/admin/myapp/owner/1/.
Now I know that if I register Object as a TabularInline or a StackedInline, then I get an editable list of the Objects this Owner owns. However, in the real version of my project, an Object has something like 25 fields, not 2, and so neither of those options is really desirable aesthetically.
What I would really like instead is to essentially have a change-list of all the Objects an Owner owns appear on the Owner's change-page. (That way I get a nice compact listing of all the Owner's Objects, and if I need to edit the details of one, I can click on its link and edit it in its own page.) Basically I want the contents of
http://mysite.com/admin/myapp/object/?owner__id__exact=1
to appear within
http://mysite.com/admin/myapp/owner/1/.
Is there a way to do this?
PS: I'm using Django 1.4 and Python 2.7.
You can define what form class and/or fields to use in each InlineModelAdmin using these attributes, and limit the amount of input fields per object that way.