I have two models: Account and Transfer.
The model Account has the attribute currency (EUR, USD, JPY, etc.).
The model Transfer has two attributes account_from and account_to.
I want to add a constraint that checks that account_from uses the same currency as account_to.
I was thinking of adding such a constraint on Transfer model:
class Meta:
constraints = [
CheckConstraint(check=Q(account_from__currency=F('account_to__currency')), name='same_currency'),
]
But that doesn't work because of error
django.core.exceptions.FieldError: Joined field references are not permitted in this query
How do I do that ? Without relying on SQL. I know how to do that in SQL but I want to use the ORM. Or is it impossible to do that with Django ORM ?
Here are the two models (simplified to avoid noise):
class Tranfer(AuditedModel):
"""
The action of moving money from one account to another
"""
account_from = models.ForeignKey(Account, related_name="outgoing", on_delete=models.CASCADE)
account_to = models.ForeignKey(Account, related_name="incoming", on_delete=models.CASCADE)
class Account(AuditedModel):
"""
Recipient for money
"""
currency = models.CharField('currency', max_length=3, choices=(('EUR', 'Euro'), ('USD', 'American Dollars'), ('JPY', 'Japan Yen')))
class Meta:
constraints = [
CheckConstraint(check=Q(account_from__currency=F('account_to__currency')), name='same_currency'),
]
Follow the following steps for the solution of your problem.
The constrain section should be in the Transfer Model not in Account model.
Check constrain for any two relational model must be avoided.
The alternative for CheckConstraint() in the Constraint array of Meta section use the function clean for validation rather than constraint.
from django.db import models
from django.core.exceptions import ValidationError
class Account(models.AuditedModel):
"""
Recipient for money
"""
currency = models.CharField('currency', max_length=3, choices=(
('EUR', 'Euro'),
('USD', 'American Dollars'),
('JPY', 'Japan Yen')
))
class Transfer(models.AuditedModel):
"""
The action of moving money from one account to another
"""
account_from = models.ForeignKey(Account, related_name="outgoing", on_delete=models.CASCADE)
account_to = models.ForeignKey(Account, related_name="incoming", on_delete=models.CASCADE)
def clean(self):
if self.account_from.currency != self.account_to.currency:
raise ValidationError("Same currency required for transaction.")
return super().clean()
Related
I am testing a method that requires me to create a fake record in my model. The model has over 40 fields. Is it possible to create a record with only the relevant model fields for the test so I don't have to populate the other fields? If so how would I apply it to this test case example.
models.py
class Contract():
company = models.CharField(max_length=255),
commission_rate = models.DecimalField(max_digits=100, decimal_places=2)
type = models.CharField(max_length=255)
offer = models.ForeignKey('Offer', on_delete=models.PROTECT)
notary = models.ForeignKey('Notary', on_delete=models.PROTECT)
jurisdiction = models.ForeignKey('Jurisdiction', on_delete=models.PROTECT)
active = models.BooleanField()
...
test.py
import pytest
from app.models import Contract
def calculate_commission(company, value):
contract = Contract.objects.get(company='Apple')
return value * contract.commission_rate
#pytest.mark.django_db
def test_calculate_commission():
#The only two model fields I need for the test
Contract.objects.create(company='Apple', commission_rate=0.2)
assert calculate_commission('Apple', 100) == 20
Try to use model_bakery to make an object record. Just populate fields you want and leave another blank, model_bakery will handle it. For the Detail, you can check this out model_bakery
I know what ForeignKeys and OneToOneFields are, as well as ManyToManyField, how they work, and when to use them. However, I am working with a project, whose Many part of the relation cannot be modified. So, suppose I want to let a user have many phone numbers, I would normally do this:
# my_app/models.py
from django.db import models
class User(Model):
...
class PhoneNumber(models.Model):
user = models.ForeignKey(User)
The problem I have is that my PhoneNumber model equivalent is from a third-party package, already populated with records, and not subclassed in my own app. That is
# third_party_django_package/models.py
from django.db import models
class PhoneNumber(models.Model):
# This cannot change
# my_app/models.py
from django.db import models
from third_party_django_package.models import PhoneNumber
class User(Model):
# These do not work -- a user can have more than one phone number
phone_number = models.ForeignKey(PhoneNumber)
phone_number = models.OneToOneField(PhoneNumber)
# This is close, but I want a phone number to belong to only one User
phone_numbers = models.ManyToManyField(PhoneNumber, related_name=...)
def clean(self):
# Validating the M2M relation costs extra queries, is slow, and
# is prone to race conditions
This is all pseudocode.
Without using yet another third-party package that accesses Django's internal members, which makes the project even less forwards-compatible, what options do I have left to achieve a proper OneToManyField with the correct schema-level constraints?
You could create another intermediate model, then make phone number OneToOneField to that model, then in that model you define User as ForeignKey.
class UserPhoneNumber(models.Model):
phone_number = models.OneToOneField(PhoneNumber)
user = models.ForeignKey(User)
It's a little cumbersome, but at least it achieves what you need.
Edit:
As #Daniel said, it's possible to do this using m2m relationship with through model, with unique_together on the fields:
class User(Model):
phone_numbers = models.ManyToManyField(PhoneNumber, through=UserPhoneNumber)
class UserPhoneNumber(Model):
phone_number = models.ForeignKey(PhoneNumber)
user = models.ForeignKey(User)
class Meta:
unique_together = ('phone_number', 'user')
This will make your life easier if you want to look up on user's phone numbers by doing numbers = user.phone_numbers.all().
I am new to Django and databases and after reading the Django documentation on models I have the following question:
Let's say I have 3 models: VehicleName, CarManufacturer and TruckManufacturer. I am trying to create a database relationship where CarMaunfacturer has many VehicleNames and also TruckManufacturer has many VehicleNames. What is the relationship here and how to define it in Django? Is it as simple as define a models.ForeignKey(VehicleName) in both CarManufacturer and TruckManufacturer?
Thanks.
from django.db import models
class CarManufacturer(models.Model):
vehicle_name = models.ForeignKey(VehicleName) # IS THIS CORRECT???
# ...
pass
class TruckManufacturer(models.Model):
vehicle_name = models.ForeignKey(VehicleName) # IS THIS CORRECT???
# ...
pass
class VehicleName(models.Model):
# ...
To do exactly what you're describing:
I am trying to create a database relationship where CarMaunfacturer has many VehicleNames and also TruckManufacturer has many VehicleNames
You'd create a nullable foreign key on VehicleName to both of your Manufacturer models:
class CarManufacturer(models.Model):
# field definitions here
class TruckManufacturer(models.Model):
# field definitions here
class VehicleName(models.Model):
car_manufacturer = models.ForeignKey(CarManufacturer, blank=True, null=True)
truck_manufacturer = models.ForeignKey(TruckManufacturer, blank=True, null=True)
Then, instances of CarManufacturer or TruckManufacturer can get the names via the vehiclename_set attribute.
For a more advanced design, I would probably try to abstract the shared manufacturer behavior into a single model, then use multi-table inheritance:
class Manufacturer(models.Model):
# shared car and truck manufacturer fields go here
class CarManufacturer(Manufacturer):
# car manufacturer specific fields go here
class TruckManufacturer(Manufacturer):
# truck manufacturer specific fields go here
class VehicleName(models.Model):
manufacturer = models.ForeignKey(Manufacturer)
See the multi-table inheritance docs for full details.
I do not think you are understanding the manufacturer to vehicle relationship property. What I think you are trying to show is that a certain Vehicle belongs to a certain manufacturer.
This type of relationship would actually be defined in the Vehicle class, as a foreign key, called manufacturer, in the Vehicle class.
In the case you are defining many vehicles under a manufacturer, you just need to rename the property to car_model or something of the like and you should be fine.
I think you have the understanding mapped out well enough. Just remember that foreign keys are only a property of one table, and say nothing about the other table itself until the relationship is established there also.
If you're working with a larger relationship, with multiple objects, you should look into using the Many-to-many field described in the django documentation.
They have an example that shows how an Articles have many Publications:
class Publication(models.Model):
title = models.CharField(max_length=30)
# On Python 3: def __str__(self):
def __unicode__(self):
return self.title
class Meta:
ordering = ('title',)
class Article(models.Model):
headline = models.CharField(max_length=100)
publications = models.ManyToManyField(Publication)
# On Python 3: def __str__(self):
def __unicode__(self):
return self.headline
class Meta:
ordering = ('headline',)
I build an app in django, but since I found out that google app engine doesn't support Django out of the box (free,cloud sql can't be used for free right?).
I decided to move to Django-nonrel, so there are few datebase Field that need converting, and I don't know how:
class Cate(models.Model):
name = models.CharField(max_length = 100)
description = models.TextField()
create_by = models.ForeignKey(User)
create_date = models.DateTimeField('cate created date')
def __unicode__(self):
return self.name
class Product(models.Model):
product_name = models.CharField(max_length = 200)
owner = models.ForeignKey(User)
cate = models.ManyToManyField(Cate)
timestamp = models.DateTimeField('product added date')
view = models.IntegerField(default = 0)
def __unicode__(self):
return self.product_name
here is the user_profile model which extends from user model
class UserProfile(models.Model):
user = models.OneToOneField(User)
cates = models.ManyToManyField('shop.Cate')
the Cate model is created by admin, UserProfile can have many cates, and same cate can belong to many users, same as product.
please help to construct these models and maybe some tips on how to use Django-nonrel
I am really new to database
There's two ways to do this. The cheaper version is to use ListFields
from djangotoolbox.fields import ListField
class UserProfile(models.Model):
user = models.OneToOneField(User)
cates = ListField(models.ForeignKey(shop.Cate))
The ListField simply stores a list of Cate ids. There's some important limitations to this.
Your entity is limited to 1MB, so this limits the number of entities in your list. In this case, it'll still be a fairly large number, especially since there's nothing else in your entity.
You can do dataastore queries against the cates field if it's indexed. However, each entity has a limit of 5000 indexes. You'll use one for the user attribute, so in this case, your cates list will be limited to have 5000 entries. I haven't hit this before so I don't know how it would fail, I presume you'd get an exception on writing your entity.
The more expensive option is to use an intermediate mapping entity. This gives you unlimited relations for the extra expense of creating/querying the mapping entities.
class UserCateMapping(models.Model)
user = models.ForeignKey(UserProfile)
cate = models.ForeignKey(Cate)
In this case, you'll need to create a new entity for each relation, and query for the UserCateMapping entities before fetching the actual Cate or UserProfile entity you actually want to use. It's going to be more costly than the first option, but you'll have unlimited mappings.
I want to make a form used to filter searches without any field being required. For example given this code:
models.py:
class Message(models.Model):
happened = models.DateTimeField()
filename = models.CharField(max_length=512, blank=True, null=True)
message = models.TextField(blank=True, null=True)
dest = models.CharField(max_length=512, blank=True, null=True)
fromhost = models.ForeignKey(Hosts, related_name='to hosts', blank=True, null=True)
TYPE_CHOICES = ( (u'Info', u'Info'), (u'Error', u'Error'), (u'File', u'File'), (u'BPS', u'BPS'),)
type = models.CharField(max_length=7, choices=TYPE_CHOICES)
job = models.ForeignKey(Jobs)
views.py:
WHEN_CHOICES = ( (u'', ''), (1, u'Today'), (2, u'Two days'), (3, u'Three Days'), (7, u'Week'),(31, u'Month'),)
class MessageSearch(ModelForm): #Class that makes a form from a model that can be customized by placing info above the class Meta
message = forms.CharField(max_length=25, required=False)
job = forms.CharField(max_length=25, required=False)
happened = forms.CharField(max_length=14, widget=forms.Select(choices=WHEN_CHOICES), required=False)
class Meta:
model = Message
That's the code I have now. As you can see it makes a form based on a model. I redefined message in the form because I'm using an icontains filter so I didn't need a giant text box. I redefined the date mostly because I didn't want to have to mess around with dates (I hate working with dates! Who doesnt?) And I changed the jobs field because otherwise I was getting a drop down list of existing jobs and I really wanted to be able to search by common words. So I was able to mark all of those as not required
The problem is it's marking all my other fields as required because in the model they're not allowed to be blank.
Now in the model they can't be blank. If they're blank then the data is bad and I don't want it in the DB. However the form is only a filter form on a page to display the data. I'm never going to save from that form so I don't care if fields are blank or not. So is there an easy way to make all fields as required=false while still using the class Meta: model = Message format in the form? It's really handy that I can make a form directly from a model.
Also this is my first serious attempt at a django app so if something is absurdly wrong please be kind :)
You can create a custom ModelForm that suit your needs. This custom ModelForm will override the save method and set all fields to be non-required:
from django.forms import ModelForm
class SearchForm(ModelForm):
def __init__(self, *args, **kwargs):
super(SearchForm, self).__init__(*args, **kwargs)
for key, field in self.fields.iteritems():
self.fields[key].required = False
So you could declare your forms by simply calling instead of the ModelForm, e.g.:
class MessageForm(SearchForm):
class Meta:
model = Message
You could also pass empty_permitted=True when you instantiate the form, e.g.,
form = MessageSearch(empty_permitted=True)
that way you can still have normal validation rules for when someone does enter data into the form.
I would give a try to the django-filter module :
http://django-filter.readthedocs.io/en/develop/
fields are not required. these are filters actually. It would look like this :
import django_filters
class MessageSearch(django_filters.FilterSet):
class Meta:
model = Message
fields = ['happened', 'filename', 'message', '...', ]
# django-filter has its own default widgets corresponding to the field
# type of the model, but you can tweak and subclass in a django way :
happened = django_filters.DateFromToRangeFilter()
mandatory, hidden filters can be defined if you want to narrow a list of model depending on something like user rights etc.
also : setup a filter on a 'reverse' relationship (the foreignkey is not in the filtered model : the model is referenced elsewhere in another table), is easy, just name the table where the foreign key of the filtered model field is :
# the 'tags' model has a fk like message = models.ForeignKey(Message...)
tags= django_filters.<some filter>(name='tags')
quick extendable and clean to setup.
please note I didn't wrote this module, I'm just very happy with it :)