How can i update the existing object of intermediate model using generic view?
class Person(models.Model):
name = models.CharField(max_length=128)
def __unicode__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __unicode__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
Currently i'm using generic views concept to update object, but I'm facing problem hoe to update field which exist in intermediate model?
If i generate modelform for Group class, then how can i update the associated field (intermediate model field) using generic view concept?
In above i want to update invite reason field
Thanks in advance
I think there are some missing views in generic or class-based views (which I highly recommend you if your are not already using them), and other people thinks in the same way...
Take a look at django-extra-views project, it implements those missing views.
Related
My question concerns the many-to-many section of the django models docs:
It is mentioned there that by using an intermediary model it is possible to query on the intermediary model's attributes like so:
Person.objects.filter(
group__name='The Beatles',
membership__date_joined__gt=date(1961,1,1))
However for the other many-to-many model (Group) a similar query results in a FieldError:
# which groups have a person name containing 'Paul'?
Group.objects.filter(person__name__contains="Paul")
Yet queries that reference the junction table explicity do work:
Person.objects.filter(membership__group__name__contains="The Beatles")
Group.objects.filter(membership__person__name__contains="Paul")
Shouldn't Group therefore have access to Person via the junction model?
Models:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __str__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
"The model that defines the ManyToManyField uses the attribute name of
that field itself, whereas the “reverse” model uses the lowercased
model name of the original model, plus '_set' (just like reverse
one-to-many relationships)." (docs: Many-to-many relationships)
So instead of
Group.objects.filter(person__name__contains="Paul")
the correct query is
Group.objects.filter(members__name__contains="Paul")
since the related model is accessible via the name of the field attribute (not the model).
Hi there Im trying to retrieve a specific object from the related model so as to render data to my view specific to that particular object, in my case I have a custom user model and a related model called Seller.
Models
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class CustomUser(AbstractUser):
is_customer = models.BooleanField(default=False)
is_seller = models.BooleanField(default=False)
class Seller(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE, blank=True, null=True)
store_name = models.CharField(max_length=120)
address = models.CharField(max_length=180)
phone = models.IntegerField(blank=True, null=True)
email = models.CharField( max_length=180, blank=True, null=True )
def __str__(self):
return self.store_name
View
#method_decorator( seller_required , name='dispatch')
class SellerDashBoardView(ListView):
model = Seller
template_name = 'seller_dashboard.html'
def get_context_data(self, *args, **kwargs):
user = CustomUser.seller_set.filter(store_name=self.request.user.username)
context = super(SellerDashBoardView, self).get_context_data( **kwargs)
context['products'] = Product.objects.filter(seller=user)[:6]
return context
This is because when you want to filter ManyToOne reverse Relation, you have to make exact the same query as you would've been done with a direct relation:
CustomUser.objects.filter(seller__store_name="Whole Foods")
# Note that would return a queryset not a single user!
# If you want a CustomUser object you will have to use either get or index the query
The doc example and explanations are provided here:
https://docs.djangoproject.com/en/3.1/topics/db/examples/many_to_one/
It is also better to use prefetch_related method to tell djano ORM that it does not have to make as many queries as number of related objects, that query should be done in 2 database queries instead of lenght of your related query:
CustomUser.objects.prefetch_related("seller_set").filter(seller__store_name="Whole Foods")
The doc link:
https://docs.djangoproject.com/en/3.1/ref/models/querysets/#prefetch-related
You probably would like to use ...seller_set.filter when you already got a CustomUser object. So if you want to filter its sellers you would use that:
...
user.seller_set.filter(store_name="Whole Foods")
That would provide you the Seller objects queryset filtered by a store name related to a specific user. Basically the same query as this:
Seller.objects.filter(user_pk=user.pk, store_name="Whole Foods")
Is there a way in Django to have multiple objects stored and manageable (in Django admin) inside another object?
Example, I have two models: Items and RMA. The RMA may have multiple Items inside of it. Each Item is unique in the sense that it is an inventoried part, so I can't just reference the same item multiple times with foreignKey (though maybe I'm misunderstanding its use/implementation).
So for now, I have an Item model:
class Item(models.Model):
serial_number = models.CharField(max_length=200)
name = models.CharField(max_length=200)
part_number = models.CharField(max_length=200)
location = models.CharField(max_length=200)
def __unicode__(self):
return self.name
And an RMA model:
class RMA(models.Model):
number = models.AutoField(primary_key=True)
items = ?????
Ultimately I'd like to be able to maintain use of the Django admin functionality to add/remove items from an RMA if necessary, so I've been staying away from serializing a list and then deserializing on display. Any help would be much appreciated.
You're modeling a has-many relationship.
This would be modeled with a Foreign Key on Item to RMA:
class Item(models.Model):
serial_number = models.CharField(max_length=200)
name = models.CharField(max_length=200)
part_number = models.CharField(max_length=200)
location = models.CharField(max_length=200)
rma = models.ForeignKey(RMA)
def __unicode__(self):
return self.name
To make it accessible in the admin of RMA you need djangos InlineAdmin functionality.
You can find examples in the django tutorial part2.
You are effectively describing a Many-To-One relation and to do this you are going to have to add the ForeignKey reference to the Item model, not to the RMA model.
You can also add a related_name to give the RMA model an attribute that you can call.
For example:
class Item(models.Model):
rma = models.ForeignKey(RMA,related_name="items")
serial_number = models.CharField(max_length=200)
# etc...
To manage the creation of these, you'll need an InlineModelAdmin form, so your admin.py file will need to look like this:
from django.contrib import admin
class ItemInline(admin.TabularInline):
model = Item
class RMAAdmin(admin.ModelAdmin):
inlines = [
ItemInline,
]
I am trying to create the following models. There is a ManyToMany relation from Entry to AUTH_USER_MODEL via the EntryLike intermediate model.
class BaseType(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
creation_time = models.DateTimeField(auto_now_add=True)
last_update_time = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class Title(BaseType):
text = models.CharField(max_length=100)
description = models.TextField()
class EntryLike(BaseType):
entry = models.ForeignKey(Entry)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
class Entry(BaseType):
title = models.ForeignKey(Title, on_delete=models.PROTECT)
text = models.TextField()
user = models.ForeignKey(settings.AUTH_USER_MODEL)
liked_by_users = models.ManyToManyField(settings.AUTH_USER_MODEL, through='EntryLike', through_fields=('entry', 'user'))
Running migrations on the above model scheme throws the error: AttributeError:'str' object has no attribute 'meta'.
Any help in resolving this error would be highly appreciated. Am new to Django & Python, but not to Web Development.
The issue is that settings.AUTH_USER_MODEL is almost certainly not a model instance. It's probably a string that constrains the choices another model can make - settings would be a strange place to leave a model definition.
To do a MTM between the user model and your field above you need need to do:
from django.contrib.auth.models import User
class Entry(BaseType):
title = models.ForeignKey(Title, on_delete=models.PROTECT)
text = models.TextField()
user = models.ForeignKey(User)
def __str__(self):
return self.title
I've added the str function so that it gives a more sensible return when you're manipulating it in admin/shell.
I'd also question whether you need the second set of fields (removed here), as you can use select related between the Entry and EntryLike join table, without any duplication of the fields - you can probably go that way, it's just a bit unnecessary.
Lastly, I'd note that the way I'm using it above just uses the default User object that comes with Django - you may wish to customise it. or extend the base class as you've done here with your own models' base class.
(All of this is predicated on AUTH_USER_MODEL not being a model instance - if it is, can you post the model definition from settings.py? )
Using the accepted answer in Class-based views for M2M relationship with intermediate model, I am able to save the Membership if I hard code an ID for its foreignkey to Group, despite not having any information for date_joined and invite_reason, but the Group for which the form is filled out never gets saved, and therefore I cannot supply Membership with the correct ID. In fact, any time I try to save thegroup, before or after the code in the accepted answer, I get the same AttributeError as the OP:
Cannot set values on a ManyToManyField which specifies an intermediary model. Use Membership's Manager instead.
Is there a way to make this code actually work? Am I being led astray? What would be the proper way to save something with a many-to-many field that goes through a mapping table? The answers I've found have confused me more than helped me.
For reference, should the question ever be deleted:
models.py
class Person(models.Model):
name = models.CharField(max_length=128)
def __unicode__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __unicode__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
Accepted Answer (With an additional line to hard code an ID)
from django.views.generic.edit import ModelFormMixin
from django.views.generic import CreateView
class GroupCreate(CreateView):
model = Group
def form_valid(self, form):
self.object = form.save(commit=False)
for person in form.cleaned_data['members']:
membership = Membership()
membership.group = self.object
membership.group_id = 108 # Added line
membership.person = person
membership.save()
return super(ModelFormMixin, self).form_valid(form)
Note: I had to add the id line because I would get the following IntegrityError without it:
Column 'group_id' cannot be null
In case it's pertinent, I am trying to save this to a MySQL database.