django populate admin listbox field from another model - django

I have a model field in which staff users can select a value from a list box. I used a fix list and the "choiches" parameter.
class Exercise(models.Model):
member = models.ForeignKey(Member)
name = models.CharField(max_length=50, choices=EXERCISES_LIST)
where EXERCISE_LIST is a sequence of tuple as required by "choiches" parameter.
Now I would like to give staff users the possibility to create this EXERCISES_LIST by themself using a model, is it possibile? and what happen if they delete an item from the list that is used by a Exercise model record?

here is the solution:
I created another model in models.py only for the name of the exercise:
class ExerciseName(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return u'%s' % (self.name)
I modified the original model adding a ForeignKey to ExerciseName
class Exercise(models.Model):
member = models.ForeignKey(Member)
name = models.ForeignKey(ExerciseName)
I registered ExerciseName model in admin.py to have the possibility to modify them in a separate admin view.
admin.site.register(ExerciseName)
So now I'm able to create a new Exercise and to choose its name from another model (ExerciseName).

Related

Django form to save m2m additional fields (using custom through/intermediary model)

I have a "Parent" model, which contains multiple "Child" models and the relationship between Parent and Child holds the order of the children. Thus, I need a custom intermediary model.
models.py:
class Parent(models.Model):
name = models.CharField
children = models.ManyToManyField(Child, through=ParentChild)
...
class Child(models.Model):
name = models.CharField()
...
class ParentChild(model.Model):
parent = models.ForeignKey(Parent, on_delete=models.CASCADE)
child = models.ForeignKey(Child, on_delete=models.CASCADE)
order_of_child = IntegerField(null=True, blank=True, unique=True, default=None)
A Parent object is created based on a form and the Child objects to be part of the Parent object are selected by checkboxes.
Now my questions are: How can the order_of_child be included in the form and rendered alongside the checkboxes and how can the relationship be correctly saved in the view?
forms.py:
class ParentForm(ModelForm):
class Meta:
model = Parent
fields = ['name', 'children']
def __init__(self, *args, **kwargs):
super(ParentForm, self).__init__(*args, **kwargs)
self.fields['name'] = forms.CharField(label='Name', widget=forms.TextInput()
self.fields['children'] = ModelMultipleChoiceField(
widget=forms.CheckboxSelectMultiple(queryset=Child.objects.all())
To save the relationship, you just first create and save the Parent, then loop through form.cleaned_data['children'] to create each ParentChild instance, assigning the index of the loop as the order.
Now for the order, that's more tricky, you'll need a widget that allows you to reorder the selection, so the default CheckboxSelectMultiple won't work because it doesn't do that. You need some javascript for that: Copy the selected options to a new div where the user can drag & drop to change the order, e.g. using a library such as jquery.ui.sortable or sortableJS/sortable. With javascript, you populate a hidden field with the selected values in the right order, which is what you submit in the end.
There's also a special django sortable multiselectfield package which I haven't tried (uses jqueryUI for sorting).

How to call a a field of one model A into another model B so that b can work as a view

I have created a model called Department, Course. Models are as follow
This is the model for departments and course
class Departments(models.Model):
Department_Id = models.IntegerField(primary_key=True)
Department_Name = models.CharField(max_length=200)
Department_Code = models.CharField(max_length=200)
class Course(models.Model):
Course_Id = models.IntegerField(primary_key=True)
Department_Id = models.ForeignKey(Departments, on_delete=models.CASCADE)
Course_Name = models.CharField(max_length=200)
Course_Code = models.CharField(max_length=200)
I want to create a model called view which can be later on called for search. I want a view model in a such a way that it consit of the data in concat form i.e. name= Department_name+ Course_Name
class View (models.model):
view_id= models.IntegerField(primary_key=True)
Name= Department_name(I want this from Departments table)
+ Course_Name(I want this from Course table)
I try using one to one relation . I would really appricate the help
It's not clear why you'd want to do that. It's never a good idea to duplicate data from one model into another one, as it can lead to inconsistencies.
You can add a ForeignKey in View to your Course model and then when you do f"{view.course.name} {view.course.department.name}" you already have your string:
class View(models.Model):
course = models.ForeignKey(Course, on_delete=models.CASCADE)
def name(self):
return f"{self.course.name} {self.course.department.name}"
Notes:
Don't call your foreign key Department_id because it's not referring to the id but to the object itself in the Django ORM: department = models.ForeignKey(Department, on_delete=models.CASCADE). As you can see, this makes reading the code much simpler: self.course.Department_id is a Department object not an integer, so self.course.department makes more sense.
Don't prefix your field names with the class, it just makes the code so much less readable: Do you prefer department.name or department.Department_name?
The View model is still a mystery to me, as you can search without it. You can search for example for courses with a matching department name like this:
Course.objects.filter(department__name__icontains="maths")
which will return all courses with "maths" in their department name.
Remove all the ids from your models, they are created automatically by Django anyway (and called id). Again, department.id is much easier to read than department.Department_id. Also in your code, you have to generate the ids yourself since you don't set them to auto-populate.

In Django admin, in a Many-to-One relationship, show a select list to pick EXISTING "Many" from the "One"

Imagine we have a model like that:
class Container(models.Model):
name = models.CharField(max_length=60)
class Element(models.Model):
container = models.ForeignKey(Container, blank=True, null=True)
Container is the One, Element is the many.
In Django admin, if I add a StackedInline with model=Element to the inlines of the Container model admin:
class Inline(admin.StackedInline):
model = Element
class ContainerAdmin(admin.ModelAdmin):
inlines = (Inline,)
admin.site.register(Container, ContainerAdmin)
I end up with a formset allowing me to enter new Element objects on the Add Container form.
Instead, I would like to be given a select widget, to pick existing Element objects.
Is that possible without introducing an extra model ?
I think you should be able to do it like this:
class ContainerAdminForm(forms.ModelForm):
class Meta:
model = Container
fields = ('name',)
element_set = forms.ModelMultipleChoiceField(queryset=Element.objects.all())
class ContainerAdmin(admin.ModelAdmin):
form = ContainerAdminForm
# register and whatnot
I don't know that I have anything like this in my project, but I'll let you know if I find something. You may also have to override the save() method on the form in order to actually save the selected Elements; I don't know if naming the field element_set (or whatever the name of the reverse relation is) will be enough.

Edit manytomany field right from Admin form for model

I have models like below
class Product(models.Model):
...
class ProductQuantity(models.Model):
product = models.ForeignKey('Product')
invoice = models.ForeignKey('Invoice')
quantity = models.IntegerField()
class Invoice(models.Model):
...
products = models.ManyToManyField(Product, through=ProductQuantity)
In django admin I would like to change quantity not by opening new dialog, which is basically how admin behaves, instead selecting one of them if any, or type there and change values directly from that window.
You can use an inline:
class ProductQuantityInline(admin.StackedInline):
model = ProductQuantity
class InvoiceAdmin(admin.ModelAdmin):
inlines = [ProductQuantityInline]
That way, you can edit the ProductQuantity directly on the Invoice admin page, without any extra dialogs.

Django: making relationships in memory without saving to DB

I have some models with relationships like this:
class Item(model.Model):
name = models.CharField()
class Group(models.Model):
item = models.ManyToManyField(Item)
class Serie(models.Model):
name = models.CharField()
chart = models.ForeignKey(Chart)
group = models.ForeignKey(Group)
class Chart(models.Model):
name = models.CharField()
I need to create a Chart object on the fly, without saving to the DB. But I can't do it because Django tries to use the objects primary keys when assigning the relationships.
I just want Group.add(Item()) to work without having to save the objects to the DB.
Is there any simple way around this?
Reviving here for the sake of future readers:
I've gotten around this use case by defining a private attribute that represents the relationship inside the classes and a property to inspect wether the object can be retrieved from the DB or resides in memory.
Here is a simple example:
class Parent(models.Model):
_children = []
name = models.CharField(max_length=100)
#property
def children(self):
if _children:
return self._children
else:
return self.children_set.all()
def set_virtual_children(self, value): # could use a setter for children
self._children = value # Expose _children to modification
def some_on_the_fly_operation(self):
print(','.join([c.name for c in self.children]))
class Children(models.Model):
parent = models.ForeignKey(Parent)
name = models.CharField(max_length=100)
This way, I can set the "virtual children" and use all the defined methods "on the fly"
EDIT: It seems that approach described here isn't enough for django to allow adding to the ManyToMany relationship.
Have you tried to add primary_key=True and unique=True to the name attribute of the Item model. Then doing Group.add(Item("item_name_here")) should work if you have the possibility to create the name on the fly.
I didn't test it, but I think your way failed because add() wants to use the primary-key which by default is the autoincrementing id that is assigned when it is saved to the database.