Queryset Many to many 'ManyRelatedManager' object is not iterable - django

I'm using Forms to filter the choices of a many to many relationship to only show the ones following a queryset. This is how my Form looks like
class ContractAdminForm(forms.ModelForm):
class Meta:
model = Contract
fields = '__all__'
def __init__(self, *args, **kwargs):
super(ContractAdminForm, self).__init__(*args, **kwargs)
self.fields['client_year_periods'].queryset =ClientYearPeriod.objects.filter(
Q(contract_id__isnull=True) | Q(contract_id=self.instance.id) &
Q(client__in=self.instance.client_entity.all()))
Error: 'ManyRelatedManager' object is not iterable
The issue is being caused by
Q(client__in=self.instance.client_entity))
I need to filter the model years using the client legal model that is connected to Client Ops.
See here how is it built
Models
class ClientEntity(models.Model):
name = models.CharField(max_length=350, verbose_name='Client Name')
countries = models.ManyToManyField(Country, blank=True)
operations_code = models.ManyToManyField(Client)
class ClientYearPeriod(models.Model):
client = models.ForeignKey(Client, on_delete=models.PROTECT, null=True)
[...]
class Contract (models.Model):
client_entity= models.ManyToManyField(ClientLegal)
client_year_periods = models.ManyToManyField(ClientYearPeriod, blank=True)
[...]
class Client(models.Model):
code = models.CharField(max_length=3, verbose_name='Client Code')
name = models.CharField(max_length=250, unique=True)
Expected Result
This contract has selected the client Someone
In the client Entity model it is related to Client = BEM
Here it should only show the ones Client: BEM Once it is saved (not when creating the contract as the client selected above it's not saved yet)

The client__in=… expects a QuerySet, self.instance.client_entity is a manager, not a QuerySet.
But you need to make a QuerySet of Clients, so:
class ContractAdminForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['client_year_periods'].queryset = ClientYearPeriod.objects.filter(
Q(contract_id__isnull=True) |
Q(contract_id=self.instance.id) &
Q(client__in=Client.objects.filter(cliententity__contract=self.instance).distinct())
)
class Meta:
model = Contract
fields = '__all__'

Related

Queryset from non-related model in __init__ modelform method

I have two model classes. They are not related models (no relationship).
# models.py
class Model1(models.Model):
description = models.TextField()
option = models.CharField(max_length=64, blank=False)
def __str__(self):
return self.option
class Model2(models.Model):
name = models.CharField(max_length=64, blank=False)
def __str__(self):
return self.name
I have respective form from where I am submitting and saving data in my table. I want to use my Model2 data to fill-in 'option' field as select field, so I am introducing below init method.
# forms.py
class Model1Form(forms.ModelForm):
def __init__(self, *args, **kwargs):
all_options = Model2.objects.all()
super(Model1Form, self).__init__(*args, **kwargs)
self.fields['option'].queryset = all_options
class Meta:
model = Model1
fields = ('description', 'option')
It does not render the dropdown on my template, so I am wondering whether it is right way to address the issue (acknowledging that models are not related to each other).

Set a field value within form __init__ function

I am trying to find out an efficient way to set a field value within form init method. My models are similar to below
class Users(models.Model):
firstname = models.CharField()
lastname = models.CharField()
class profile(models.model):
user = models.ForeignKey(Users, on_delete=models.PROTECT)
class logindetails(models.model):
user = models.ForeignKey(Users, on_delete=models.PROTECT)
profile = models.ForeignKey(profile, on_delete=models.PROTECT)
login_time = models.DateField(auto_now=True)
My form is like as below:
class LoginForm(forms.ModelForm):
class Meta:
model = logindetails
fields = [__all__]
def __init__(self, *args, **kwargs):
self._rowid = kwargs.pop('rowid', None)
super(LoginForm, self).__init__(*args, **kwargs)
instance = profile.objects.get(id=self._rowid)
self.fields['user'] = instance.user <--- Facing difficulties here
Any help will be appreciated.
Django had built-in ways of setting initial form values, the documentation is available here: https://docs.djangoproject.com/en/3.0/ref/forms/api/#dynamic-initial-values

iterating over a queryset and replacing foreign keys with their related objects django rest framework

I have the following models:
class STUser(AbstractBaseUser):
email = models.EmailField(unique=True)
name = models.CharField(max_length=255)
...
class VenuePermissions(models.Model):
user = models.ForeignKey(STUser, on_delete=models.CASCADE)
venue = models.ForeignKey(Venue, on_delete=models.CASCADE, blank=True, null=True)
...
class Venue(models.Model):
name = models.CharField(max_length=100)
description = models.CharField(max_length=1500, null=True, blank=True)
...
I am then prefetching the related venuepermission objects to a STUser object in a view
class VenueUserList(APIView):
def get(self, request, *args, **kwargs):
objects = STUser.objects.prefetch_related('venuepermissions_set').all()
...
Then I want to take the returned query set and replace the venue FKs with their objects.
for item in objects:
for permission in item_venuepermissions_set:
permission_venue = Venue.objects.get(pk=permission_venue)
print(repr(objects))
...
return Response({})
but my approach is clearly wrong. how do I properly access a field of a queryset and change its value?
As you can see I need to do it twice. Once to access the current objects permission_set and again to access that permission_set venue field and override it.
I do not want any of this changing the database. This is just to return the values.
update:
I made the following changes to get access to the individual fields:
for item in objects:
for permission in item.venuepermissions_set:
permission.venue = Venue.objects.get(pk=permission.venue)
print(repr(objects))
return Response({})
the new error I have is:
TypeError: 'RelatedManager' object is not iterable
trying to resolve
Answering all my questions today:
class VenueUserList(APIView):
def get(self, request, *args, **kwargs):
objects = STUser.objects.prefetch_related('venuepermissions_set').all()
for item in objects:
for permission in item.venuepermissions_set.all():
permission.venue = Venue.objects.get(pk=permission.venue_id)
return Response({})
is the way to do it

Validating a Django field dependently on context

I write the code to edit the list of users which belong to a team. For this I create a form, as below:
class Organization(models.Model):
name = models.CharField(blank=False, verbose_name=_("Name"), help_text=_('Organization Name'), max_length=256)
class Team(models.Model):
organization = models.ForeignKey('Organization')
name = models.CharField(blank=False, verbose_name=_("Name"), help_text=_('Team Name'), max_length=256)
users = models.ManyToManyField('User', related_name='teams')
def __str__(self):
return self.name
class TeamUsersForm(forms.ModelForm):
class Meta:
model = Team
fields = ['users']
users = forms.ModelMultipleChoiceField(queryset=User.objects.filter(request.user.organization), required=False)
def clean_users(self):
users = self.cleaned_data['users']
if users.exclude(organization=request.user.organization):
raise ValidationError(_("Cannot add user from another organization"))
return users
The code above should look into request value to determine the current organization and restrict display and model store only to users from the same organization.
But the above code cannot work, because the value of request is not known at class loading time.
What do you suggest to do?
I thought of two variants:
create a local (to a function) class like the above class TeamUsersForm
dismiss using Django forms for this altogether and use more low-level API
Overide the __init__ of the TeamUsersForm and access request there.
class TeamUsersForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request')
super().__init__(*args, **kwargs)
self.fields['users'] = forms.ModelMultipleChoiceField(queryset=User.objects.filter(self.request.user.organization), required=False)
This implies that when you instantiate your form, you should it this way:
# somewhere in your views.py, probably
f = TeamUsersForm(request.POST, request=request)

Enforcing related model creation

I'd like to add multiple dealers support from the dashboard following the oscar's documentation:
You’ll need to enforce creating of a StockRecord with every Product.
When a Product is created, Stockrecord.partner gets set
to self.request.user.partner (created if necessary), and hence the
connection is made
I don't know how to enforce the StockRecord creation. oscar has a dashboard that replaces the django admin, this is an excerpt(first lines) of the view used to create/update a Product:
class ProductCreateUpdateView(generic.UpdateView):
"""
Dashboard view that bundles both creating and updating single products.
Supports the permission-based dashboard.
"""
template_name = 'dashboard/catalogue/product_update.html'
model = Product
context_object_name = 'product'
form_class = ProductForm
category_formset = ProductCategoryFormSet
image_formset = ProductImageFormSet
recommendations_formset = ProductRecommendationFormSet
stockrecord_formset = StockRecordFormSet
So the Product creation view would display the StockRecord formset, but I can create/update the Product without creating a StockRecord object. I'd like to show an error message when this occurs.
StockRecord form/formset:
class StockRecordForm(forms.ModelForm):
def __init__(self, product_class, *args, **kwargs):
super(StockRecordForm, self).__init__(*args, **kwargs)
# If not tracking stock, we hide the fields
if not product_class.track_stock:
del self.fields['num_in_stock']
del self.fields['low_stock_threshold']
else:
self.fields['price_excl_tax'].required = True
self.fields['num_in_stock'].required = True
class Meta:
model = StockRecord
exclude = ('product', 'partner', 'num_allocated')
BaseStockRecordFormSet = inlineformset_factory(
Product, StockRecord, form=StockRecordForm, extra=1)
class StockRecordFormSet(BaseStockRecordFormSet):
def __init__(self, product_class, *args, **kwargs):
self.product_class = product_class
super(StockRecordFormSet, self).__init__(*args, **kwargs)
def _construct_form(self, i, **kwargs):
kwargs['product_class'] = self.product_class
return super(StockRecordFormSet, self)._construct_form(
i, **kwargs)
StockRecord model(excerpt):
class AbstractStockRecord(models.Model):
product = models.ForeignKey(
'catalogue.Product', related_name="stockrecords",
verbose_name=_("Product"))
partner = models.ForeignKey(
'partner.Partner', verbose_name=_("Partner"),
related_name='stockrecords')
partner_sku = models.CharField(_("Partner SKU"), max_length=128)
price_currency = models.CharField(
_("Currency"), max_length=12, default=settings.OSCAR_DEFAULT_CURRENCY)
price_excl_tax = models.DecimalField(
_("Price (excl. tax)"), decimal_places=2, max_digits=12,
blank=True, null=True)
price_retail = models.DecimalField(
_("Price (retail)"), decimal_places=2, max_digits=12,
blank=True, null=True)
What you want to do is ensure there is at least 1 valid formset submitted when saving the main form?