The queryset for the 'jurisdiction' field is set below in the initialization. The queryset is dependent on the id that is passed in, which comes from a specific link that a user clicks. As a result, I can't define a singular queryset within the forms.ModelChoiceField(), but it seems that django requires me to do this.
class TaxForm (forms.ModelForm): #Will be used for state tax and other taxes
jurisdiction = forms.ModelChoiceField(queryset=?????)
class Meta:
model = Tax
exclude = ('user', 'taxtype',)
def __init__(self, *args, **kwargs):
self.taxtype = kwargs.pop('taxtype',None)
super(TaxForm, self).__init__(*args, **kwargs)
if int(self.taxtype) == 1:
self.fields['jurisdiction'].choices = [(t.id, t) for t in State.objects.all()]
elif int(self.taxtype) == 2:
self.fields['jurisdiction'].choices = [(t.id, t) for t in Country.objects.all()]
else:
self.fields['jurisdiction'].choices = [(t.id, t) for t in State.objects.none()]
How can I indicate that I want the jurisdiction field to be a dropdown, but not specify one queryset within the forms.ModelChoiceField()? Alternatively, how can I make the queryset that is referenced in forms.ModelChoiceField() refer to the queryset that I initialize based on the taxtype?
Thanks!
Here is my tax model
class Tax(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, default=None)
jurisdiction = models.CharField(max_length=120, null=True, blank=True)
name = models.CharField(max_length=200)
rate = models.DecimalField(max_digits=10, decimal_places=2)
basis = models.CharField(max_length=120, null=True, blank=True)
regnumber = models.CharField(max_length=200, null=True, blank=True) #tax number that will appear on customer invoice
taxtype = models.IntegerField(blank=True, null=True) # 0 is other, 1 is state, 2 is federal
def __str__(self):
return '{} - {}'.format(self.user, self.name)
As I mentioned, ModelChoiceField is not the right thing to do here. That's for allowing the user to choose from related items from a single model that will be saved into a ForeignKey. You don't have a ForeignKey, and what's more you're setting the choices attribute in your init rather than queryset. You should make it a plain ChoiceField with an empty choices parameter:
jurisdiction = forms.ChoiceField(choices=())
(For the sake of completeness: if you did need to use ModelChoiceField you can put anything you like into the queryset parameter when you're overwriting it in __init__, because it will never be evaluated. But managers have a none method which returns an empty queryset, so you could do queryset=State.objects.none().)
Related
I am trying to override the save method in a model with logic to update a couple of many to many fields. Using print statements I can see values updating as expected but the values are not persisted after save.
In the below model the change_access_flag is changing as expected with a signal, the prints are executing with the appropriate values, but the allowed_departments and allowed_communities fields are not updating with the printed values.
Model
class Person(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
full_name = models.CharField(null=True, blank=True, max_length=50)
payroll_id = models.CharField(null=True, max_length=20)
position = models.ForeignKey(Position, null=True, on_delete=models.SET_NULL)
primary_community = models.ForeignKey(Community, null=True, on_delete=models.CASCADE, related_name="primary_community")
region = models.CharField(max_length=2, choices=RegionChoices.choices, blank=True, null=True)
allowed_communities = models.ManyToManyField(Community, blank=True, related_name="allowed_community")
allowed_departments = models.ManyToManyField(Department, blank=True)
access_change_flag = models.BooleanField(default=False)
def __str__(self):
return f'{self.user.first_name} {self.user.last_name}'
class Meta:
verbose_name_plural = "People"
ordering = ['position__position_code', 'user__last_name', 'full_name']
def save(self, *args, **kwargs):
#Set Full Name field
if self.user.last_name:
self.full_name = f'{self.user.first_name} {self.user.last_name}'
super().save(*args, **kwargs)
#Change flag set in signals, set for events that require updating access settings
if self.access_change_flag:
self.access_change_flag = False
#Allowed community access
access_level = self.position.location_access_level
self.allowed_communities.clear()
if access_level == 'R':
if self.primary_community.community_name == '#':
region = self.region
else:
region = self.primary_community.region
if region is not None:
communities = Community.objects.filter(region=region)
self.allowed_communities.set(communities)
self.allowed_communities.add(self.primary_community)
elif access_level == 'A':
communities = Community.objects.filter(active=True)
self.allowed_communities.set(communities)
else:
communities = self.primary_community
self.allowed_communities.add(communities)
print(self.allowed_communities.all())
#Allowed department access
dept_access = self.position.department_only_access
if dept_access:
depts = [self.position.department]
else:
depts = Department.objects.filter(active=True)
self.allowed_departments.set(depts)
print(self.allowed_departments.all())
super().save(*args, **kwargs)
I have tried variations of set, clear, add, moving the super.save() around, and placing the logic in a signal but nothing seems to work. I have tested initiating save from both a model form through a view and admin.
Let me answer in quotes. You can find the source in this section.
If you wish to update a field value in the save() method, you may also
want to have this field added to the update_fields keyword argument.
This will ensure the field is saved when update_fields is specified.
Also read here
Specifying update_fields will force an update.
So try to call the super().save(*args, **kwargs) method at the end with defining the argument update_fields. This will force the update of your model regarding the specified fields.
Let me know how it goes.
I have a database representing financial transactions. Columns representing payee and category are non-optional.
However, part of my app's functionality will be to ingest external spreadsheets of transactions which do not already have payee and category information. I would then populate a form where the user will select correct payees and categories through drop-down menus, and then save the completed information to the database.
Is the correct approach to simply create two separate but equivalent classes (see below)? Or is there some way to make one a sub-class to another, despite the fact that one is connected to a database and the other is not.
# An initial class representing a transaction read from an Excel sheet
# Payee and category information are missing at this stage, but will be filled
# in by the user later on
class TransactionFromSpreadsheet:
def __init__(self, date, amount):
self.date = date
self.amount = amount
self.payee = None
self.category = None
# The Django class that will be instantiated once all the user has completed all
# necessary information
class Transaction(models.Model):
date = models.DateField()
amount = models.DecimalField(max_digits=14, decimal_places=2)
category = models.ForeignKey('Category', on_delete=models.CASCADE)
payee = models.ForeignKey('Payee', on_delete=models.CASCADE)
One could use optional foreign keys and a custom manager to provide an easy way to query the "incomplete" or "complete" transactions.
class TransactionQueryset(models.query.QuerySet):
def complete(self):
return self.filter(category__isnull=False,
payee__isnull=False)
def incomplete(self):
return self.filter(category__isnull=True,
payee__isnull=True)
class TransactionManager(models.Manager):
def get_queryset(self):
return TransactionQueryset(self.model, using=self._db)
def complete(self):
return self.get_queryset().complete()
def incomplete(self):
return self.get_queryset().incomplete()
class Transaction(models.Model):
date = models.DateField()
amount = models.DecimalField(max_digits=14, decimal_places=2)
category = models.ForeignKey('Category', on_delete=models.CASCADE,
blank=True, null=True)
payee = models.ForeignKey('Payee', on_delete=models.CASCADE,
blank=True, null=True)
objects = TransactionManager()
And if you now need an incomplete transaction you could easily get these in a view:
def view_incomplete(request):
incompletes = Transaction.objects.incomplete()
return render(request, 'incomplete_template.html',
{'incompletes': incompletes})
It is now very comfortable to gather all heavily used filter conditions in the queryset and manager class.
And if you have non complementary filter conditions you could even chain the manager functions.
my models
class Player(TimeStampedModel):
name = models.CharField(max_length=200)
email = models.CharField(max_length=200)
email_verified = models.BooleanField(default=False, blank=True)
phone = models.CharField(max_length=200)
phone_verified = models.BooleanField(default=False, blank=True)
company_id = models.ImageField(upload_to=get_file_path_id_card, null=True,
max_length=255)
company_id_verified = models.BooleanField(default=False, blank=True)
team = models.ForeignKey(Team, related_name='player', on_delete=models.DO_NOTHING)
def __str__(self):
return self.name
this is my model , how to filter data in multiple model?
You can use a Queryset to filter by modal object's field.
You can use this to also filter relationships on models.
In your example, you can do a filter of all the Player entries that have a Character that have Weapon with strength > 10
Player.objects.filter(character__weapon__strength__gt=10)
You can also separate them out into 3 variables for readability purposes.
player_q = Player.objects.filter(character__isnull=False)
ch_q = player_q.filter(weapon__isnull=False)
wpn_dmg = ch_q.filter(strength__gt=10)
Please note that filters are lazy and thus don't return actual model instances untill they're evaluated. I think in this case gt returns an instance.
This documentation goes over all the fieldset lookups you can do with QuerySet object methods filter(), get(), and exclude()
I have the following machine model.
class Machine(models.Model):
operators = models.ManyToManyField(User, related_name='machines', blank=True)
elasticsearch_id = models.PositiveIntegerField(default=None, null=True, blank=True)
company = models.ForeignKey(Company, default=None, null=True, blank=True,on_delete=models.SET_DEFAULT)
machine_brand = models.CharField(max_length=200, null=False)
machine_model = models.CharField(max_length=200, default='')
machine_picture = models.URLField(max_length=200, null=True)
tools = models.ManyToManyField('Tool', default=None, blank=True)
clustered_tags = JSONField(null=True)
elasticsearch_tags = JSONField(null=True, blank=True, default=DEFAULT_TAG_MAP)
machine_slug = models.SlugField()
With the following serializer.
class MachineSerializer(serializers.ModelSerializer):
class Meta:
model = Machine
fields = '__all__'
In my views, I am filtering the data on the company the logged in users belongs to. Now, I want to serialize the object and return it to the client. However, I don't know beforehand whether the queryset is a list of objects or a single object so that I can set the many flag of the serializer to true or false.
#api_view(['GET','POST'])
def manage_operators(request):
user_machines = Machine.objects.filter(company=request.user.company)
user_machines_ser = MachineSerializer(user_machines, many=True)
return Response({'machines': user_machines_ser.data})
Is there any elegant way to solve this? I could solve it this way but there must be a better way of doing it.
if len(user_machines) > 0 :
user_machine_ser = MachineSerializer(user_machines, many=True)
else:
user_machine_ser = MachineSerializer(user_machines, many=False)
Any input much appreciated!
Since you are fetching a QuerySet every time, you don't have to set many=False if there is only one item in the QuerySet.
So you can safely use
user_machine_ser = MachineSerializer(user_machines, many=True)
everytime, no matter how many objects are in the QuerySet.
Since you are passing a QuerySet, you can use the count() [Django doc] method in the __init__() method of MachineSerializer by overriding it.
class MachineSerializer(serializers.ModelSerializer):
class Meta:
model = Machine
fields = '__all__'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if kwargs['instance'].count() > 1: # count() method used here <<<<<
kwargs['many'] = True
else:
kwargs['many'] = False
I'm trying to create a transaction history for each transaction on a Django based marketplace.
I thought the best way of keeping track of this data was to override the save() function and create a Transaction record.
class Transaction(models.Model):
item = models.ManyToManyField(Item, blank=True)
buyer = models.ManyToManyField(User, related_name='buyer')
seller = models.ManyToManyField(User, related_name='seller')
description = models.CharField(max_length=500)
purchase_date = models.DateField(auto_now_add=True)
value = models.DecimalField(max_digits=7, decimal_places=2)
def save(self, *args, **kwargs):
self.buyer.money+=self.value
self.seller.money-=self.value
super(Transaction, self).save(*args, **kwargs)
Am I going about this all wrong? Currenlty I get...
'Transaction' instance needs to have a primary key value before a many-to-many relationship can be used.
You have to save your object before you can go through many-to-many relationships.
Please explain how you can have multiple buyers and sellers on a single transaction. (For the rest of this answer, I'm assuming that there aren't and you meant for these to be ForeignKey fields.)
The related names for buyer and seller are not clear. See below.
I'm not sure what description is for. Is it different from the item list?
item should be called items, since it can be plural, and you might want to create a custom junction table (using the "through" parameter) with a quantity field.
You forgot to save the related objects.
Modified version:
class Transaction(models.Model):
items = models.ManyToManyField(Item, through='TransactionItem', blank=True)
buyer = models.ForeignKey(User, related_name='transactions_as_buyer')
seller = models.ForeignKey(User, related_name='transactions_as_seller')
description = models.CharField(max_length=500)
purchase_date = models.DateField(auto_now_add=True)
value = models.DecimalField(max_digits=7, decimal_places=2)
def save(self, *args, **kwargs):
super(Transaction, self).save(*args, **kwargs)
self.buyer.money += self.value
self.buyer.save()
self.seller.money -= self.value
self.seller.save()
class TransactionItem(models.Model):
transaction = models.ForeignKey(Transaction)
item = models.ForeignKey(Item)
quantity = models.IntegerField()
The buyer and seller fields are many to many fields so self.buyer will never work, I think you were meaning to use ForeignKey instead of ManyToManyField.