I have this model:
class Invoice(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL)
data = models.TextField(default=None, blank=True, null=True)
number = models.PositiveIntegerField(default=0, null=False)
What I need is to auto-increment the field number for each separated user. The rationale is that each user has a list of Invoice, starting from number=1 to number=latest.number+1.
I do known about F() expressions, but can't figure out how to reference the latest/greatest number for each specific user. Maybe Invoice.objects.filter(owner=request.user).aggregate(Max('number')) is the path, but how do I ensure there is no race conditions between Max() and F()?
You can achieve this and similar functions by overriding save method in model and writing your custom logics to it.
class Invoice(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL)
data = models.TextField(default=None, blank=True, null=True)
number = models.PositiveIntegerField(default=0, null=False)
def save(self, *args, **kwargs):
if self.pk:
self.number += 1
# Write all your logic here, like handeling max value etc
return super(Invoice, self).save(*args, **kwargs)
you can get your first or last object like this:
# For last Object
Model.objects.latest('field') # Field can be id or pk or ...
# For first Object
Model.objects.all().first() # You can also use it on filter
A simple solution is you can make the number field as the primary key since its nature would be similar.
class Invoice(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL)
data = models.TextField(default=None, blank=True, null=True)
number = models.IntegerField(primary_key=True)
Or, you can make number as AutoField or BigAutoField.
number = models.AutoField()
Related
I'm writing and app to manage my network equipments. I created a model, RJ45port, which I can add to my equipment as needed. A RJ45port can be plugged into an other RJ45port and only one.
Here is the model I created :
class RJ45port(models.Model):
plugged_into = models.OneToOneField('self', on_delete=models.SET_NULL, blank=True, null=True)
When I "plug" a RJ45port into another, I want the second one to have "plugged_into" set to the first one. I want the relation to be symmetrical. If I "unplug", I want both of the RJ45 ports to have "plugged_into" set to null, or blank.
I found a bit of code, it might be a hint :
def save(self, *args, **kwargs):
super(RJ45port, self).save()
self.plugged_into.plugged_into = self
To be honest I'm a bit lost here and it's the final step I need to get this app functional...
You are best suited just making a model plug_into() method, and then using it to "plug" one instance into another, as well as an unplug() method.
Example:
class RJ45port(models.Model):
plugged_into = models.OneToOneField('self', on_delete=models.SET_NULL, blank=True, null=True)
def plug_into(self, instance):
self.plugged_into = instance
instance.plugged_into = self
self.save(update_fields=['plugged_into'])
instance.save(update_fields=['plugged_into'])
return [self.plugged_into, instance.plugged_into]
def unplug(self):
self.plugged_into.plugged_into = None
self.plugged_into = None
self.plugged_into.save(update_fields=['plugged_into'])
self.save(update_fields=['plugged_into'])
return [self.plugged_into, instance.plugged_into]
And then you can call it like this:
port_1 = Port.objects.all()[0] # First port
port_2 = Port.objects.all()[1] # Second port
port_1.plug_into(port_2) # Should return [instance, instance]
port_1.unplug() # Should return [None, None]
You are right. Simply override the save method. But call super().save() at the end:
class RJ45port(models.Model):
plugged_into = models.OneToOneField('self', on_delete=models.SET_NULL, blank=True, null=True)
def save(self, *args, **kwargs):
self.plugged_into.plugged_into = self
super(RJ45port, self).save()
Another option is to use a related_name so you can make a reverse access from the referenced instance, so you can say that the relationship becomes "symetrical". The only downside is that you can't use the same name to reference both connections:
class RJ45port(models.Model):
plugged_into = models.OneToOneField('self', on_delete=models.SET_NULL, blank=True, null=True, related_name='plugged_from')
In this example, plugged_from can be queried like any other field from the referenced instance.
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().)
I have a model with 2 char-fields. I want to give them default values when a row is getting created. This default values are sort of IDs which depends on time. This ID should never change. i.e. the default value should not be applied when subsequent updates happen on the row.
I'm trying to use update_or_create but, the defaults while creating & updating are not same. How can I put these Ids only while creating and ignore while updating?
I'm referring to this answer but no luck.
EDIT:
Following is the code for reference:
Model:
class UsersModel(models.Model):
id = models.CharField(db_column="id", max_length=25, primary_key=True)
key = models.CharField(db_column="key", max_length=100)
a = models.CharField(db_column="a",max_length=25, null=True, blank=True)
b = models.BigIntegerField(db_column="b", null=True, blank=True)
Views:
def post(self, request, format=None):
UsersModel.objects.update_or_create(a="a_val",defaults={"b":"b_val"})
Here, I want the id & key to take default values when the row is being created. When it is being updated, only b should get updated as shown in above code.
The same can be achieved by overiding the save function of models.Model to ensure that an id and key is given a value(default) in your case if the model is being created.
class UsersModel(models.Model):
id = models.CharField(db_column="id", max_length=25, primary_key=True)
key = models.CharField(db_column="key", max_length=100)
a = models.CharField(db_column="a",max_length=25, null=True, blank=True)
b = models.BigIntegerField(db_column="b", null=True, blank=True)
def save():
if not self.id:
self.id=get_default('id')
if not self.key:
self.key=get_default('key')
super(UsersModel, self).save(*args, **kwargs)
This will ensure when you create and update, the first time save is called, these variables are assigned a default value before getting saved in the db.
I'm using Django 1.5 and am struggling with something that I guess is pretty basic.
I've got following Persons model:
class Person(models.Model):
contact_person = models.ManyToManyField(ContactPerson)
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
...
The goal is to add a partner of the person to the model. I don't want the partner or the person to be superior to the other; they should be equal.
In other words, when looking up a person, his/her partner should appear as well (if there is one). I need to be able to add the partner once, either at the woman's or at the men's side (I don't want to link them two times).
I've searched the documentation for OneToOneFields, but recursive OneToOnes don't seem to be supported, i.e. I get a NameError ("name 'Person' is not defined") when I try:
partner = models.OneToOneField(Person, blank=true, null=true)
Can anyone point me in the right direction?
Try this:
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
...
partner = models.OneToOneField('self', null=True, blank=True)
def save(self, checkPartner = True, *args, **kwargs):
super(Person, self).save()
if self.partner and checkPartner:
self.partner.partner = self
self.partner.save(checkPartner = False)
Null true on partner field, has to be enabled because the first person to be saved will not have any partner.
checkPartner parameter has been added so that save() doesn't fall into an infinite loop
https://docs.djangoproject.com/en/dev/ref/models/fields/#foreignkey
partner = models.OneToOneField('Person', blank=true, null=true)
or in alternative, if you're on the same class
partner = models.OneToOneField('self', blank=true, null=true)
quotes are everything, in this case
def save(self, *args, **kwargs):
# call to super, we want self.partner to be set
super(Person, self).save(*args, **kwargs)
# this is necessary to avoid infinite save loops on partner's save call
# at this point, you have a partner
# this won't work if your partner has already a partner
# but it's easy to go from here
if not self.partner.partner:
self.partner.partner = self
self.partner.save()
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.