For example, I have a lot of interrelated tables in my project
class A(models.Model):
name = models.models.CharField(max_length=16)
class B(models.Model):
name = models.models.CharField(max_length=16)
a = models.ForeignKey(A, on_delete=models.CASCADE)
class C(models.Model):
name = models.models.CharField(max_length=16)
b = models.ForeignKey(B, on_delete=models.CASCADE)
and so on.
I need to test model C and have no interest in A and B models. Is there any chance to mock model B that can be used when creating model C objects?
I mean I'd like to create few objects without building a massive base just to test one tiny model.
You can use bakery
from model_bakery import baker
AmodelInstance = baker.make(A)
If it is only for some tests and you only need a few instances you could do something like this:
model_a_instance = A(name='somename')
model_b_instance = B(name='somename2', a=model_a_instance)
model_c_instance = C(name='somename3', b=model_b_instance)
Nonetheless, if you need to save model_c in the DB make sure to save model_a and model_b also.
If you are going to need these models more extensively I would recommend creating a factory class to populate your models.
Hopefully this helped.
After couple of days of research I don't have an answer to my question, but I think I found a tool that takes care of 'preparing the base' before actual testing or greatly simplifies it.
I have about 1000 lines of test code in my project and I decided to switch to pytest and rewrite tests almost from scratch, so, I'm actually doing what I hoped to avoid, but it's acceptable for me in this case.
Instead of creating queryset objects via django ORM I utilize Factory Boy to define factories that resolve database relations in the way I want.
For the example case I would implement something like that:
import factory
class AFactory(factory.django.DjangoModelFactory):
class Meta:
model = A
class BFactory(factory.django.DjangoModelFactory):
class Meta:
model = B
a = factory.SubFactory(AFactory)
class CFactory(factory.django.DjangoModelFactory):
class Meta:
model = C
b = factory.SubFactory(BFactory)
So, to test C model you're just creating C model object via CFactory.create(), and all 'chain' stuff is handled using SubFactories.
However, example models have no any constraints, unlike real database tables. Here's an example for more descriptive answer:
class Country(models.Model):
name = models.CharField(choices=(('BY', 'Belarus'), ('UA', 'Ukraine')),
max_length=64, unique=True)
class Employee(models.Model):
name = models.CharField(max_length=64)
email = models.EmailField(max_length=100, unique=True)
country = models.ForeignKey(Country, on_delete=models.CASCADE)
class Supervisor(models.Model):
name = models.CharField(max_length=64)
email = models.EmailField(max_length=100, unique=True)
country = models.ForeignKey(Country, on_delete=models.CASCADE)
employee = models.ForeignKey(Employee, on_delete=models.CASCADE)
If I would use same approach as with A B C example and executed something like SupervisorFactory.create() at least 2 times I violated Country.name and Employee.email constraints. 2 SupervisorFactory calls would try to create 4 countries, but it's possibly to create only 3 - 'BY', 'UA', and empty string that is factory boy's default. Thus, you should specify the rules factories generate data by:
class CountryFactory(factory.django.DjangoModelFactory):
class Meta:
model = Country
django_get_or_create = ('name',)
name = factory.Iterator('BY', 'UA')
class EmployeeFactory(factory.django.DjangoModelFactory):
class Meta:
model = Employee
email = factory.Sequence(lambda nums: 'employee.%04d#employee.io' % nums)
country = factory.SubFactory(CountryFactory)
class Supervisor(factory.django.DjangoModelFactory):
class Meta:
model = Supervisor
email = factory.Sequence(lambda nums: 'supervisor.%04d#supervisor.io' % nums)
country = factory.SubFactory(CountryFactory)
employee = factory.SubFactory(EmployeeFactory)
Hope this will save your time.
Related
I'm new to Django
I'm currently using django 3.2.6. I want make multiple instances of route_stop model and store in SchoolRouteStop.route_graph model.I don't want use ForeignKey because i want to make somthing like like nested dict.
from django.db import models
class geo_fence(models.Model):
radius = models.FloatField()
class geo_location(models.Model):
latitude = models.FloatField()
longitude = models.FloatField()
class address(models.Model):
entity = models.fields.CharField(max_length=100)
apt_plot = models.fields.CharField(max_length=100)
street = models.fields.CharField(max_length=100)
city = models.fields.CharField(max_length=100)
state = models.fields.CharField(max_length=2) #state name in short code
zip_code = models.fields.IntegerField()
class route_stop(models.Model): # this for multiple bus stops
route_stop_id = models.fields.IntegerField()
school_id = models.fields.CharField(max_length=100)
route_number = models.fields.CharField(max_length=100)
school_route_stop_uuid = models.fields.CharField(max_length=100, primary_key=True)
registered_arrival_time = models.TimeField()
time_from_src = models.FloatField()
is_school = models.BooleanField(default=False)
geo_fence = models.ForeignKey(geo_fence, on_delete =models.CASCADE)
geo_location = models.ForeignKey(geo_location, on_delete = models.CASCADE)
address = models.ForeignKey(address, on_delete = models.CASCADE)
class SchoolRouteStop(models.Model):
school_id = models.CharField(max_length=100)
school_route_number = models.IntegerField()
route_type = models.CharField(max_length=2)
route_id = str(school_id)+'_'+str(school_route_number)+str(route_type)
route_graph= models.ForeignKey(route_stop,related_name='School', on_delete = models.CASCADE)
# Create your models here.
You have to use a ForeignKey here because you will lose all the Django ORM features and performances if you try to hack this.
Trying to use a JSONField or something else instead would also mean losing integrity constraints you would need to implement yourself, which you really want to avoid.
The way Django works is you implement your models to be stored efficiently in the database, then you use views & serializers to manipulate them.
Your models need to be refined, I really have a hard time understanding their real purpose because there are id fields everywhere (that should also probably be ForeignkeyField), and everything seems a little confusing.
For example, why is school_route_stop_uuid a CharField when UUIDField does exist?
Why is route_id not a property?
Also, make sure to follow the naming conventions in Python, it will make you code way cleaner. According to PEP 8 (https://www.python.org/dev/peps/pep-0008/#class-names):
Class names should normally use the CapWords convention.
I am struggling to understand django models relationship.
I have this arborescence:
A train have cars, and those cars are divided into parts. Then those parts all contains different references.
Like, for exemple, all the trains have the 6 cars, and the cars 6 parts. Each part have x reference to be associated.
I would like to use all of them in a template later on, where the user can select the train, the car and the part he worked on, then generate a table from his selections with only the references associated to the parts he selected.
It should update the train and the car (I'm trying to update a stock of elements for a company)
I dont really understand which model field give to each of them. After checking the doc, Ive done something like this but i am not convinced:
class Train(Car):
train = models.CharField(max_length=200)
id = models.CharField(primary_key='True', max_length=100)
selected = models.BooleanField()
class Meta:
abstract = True
class Car(Part):
car = models.CharField(max_length=200)
id = models.CharField(primary_key='True', max_length=100)
selected = models.BooleanField()
class Meta:
abstract = True
class Part(Reference):
part = models.CharField(max_length=200)
id = models.CharField(primary_key='True', max_length=100)
selected = models.BooleanField()
class Meta:
abstract = True
class Reference(models.Model):
reference = models.CharField(max_length=200)
id = models.CharField(primary_key='True', max_length=100)
selected = models.BooleanField()
def __str__(self):
return self.reference
Can someone please help me understand this so I can do well ? Thanks!!
1-)if you add abstract = True in your Model Meta class, your class doesn't created on database as a table. If you store data for any class, you mustn't define abstract = True.
2-)For relations, you can use models.ForeignKey . If you add a class into brackets of another class, it names: inheritance.(You can think like parent-child relation). In database management, we can use foreignkey for one-to-many relationship.
3-)In Django ORM, id field automatically generated. So you don't need to define id field.
If I understand correctly, also you want to store parts of user's selected.
So, your model can be like that:
class Train(models.Model):
name = models.CharField(max_length=200) # I think you want to save name of train
class Car(models.Model):
train = models.ForeignKey(Train,on_delete=models.Cascade)
name = models.CharField(max_length=200)
class Part(models.Model):
car = models.ForeignKey(Car,on_delete=models.Cascade)
name = models.CharField(max_length=200)
class Reference(models.Model):
part = models.ForeignKey(Part,on_delete=models.Cascade)
name = models.CharField(max_length=200)
def __str__(self):
return self.reference
#addtional table for storing user's references
class UserReference(models.Model):
user = models.ForeignKey(User,on_delete=models.Cascade)
reference = models.ForeignKey(Reference,on_delete=models.Cascade)
name = models.CharField(max_length=200)
With this definitions, you can store user's definition on UserReference table. And with Django Orm, you can access train object from UserReferenceObject.
#user_reference: UserReference object like that result of UserReference.objects.first()
user_reference.reference.part.car.train.name
I am trying to show a M2M field in a django-table2 as seen in Django-tables2: How to use accessor to bring in foreign columns? and Accessing related models with django-tables2
Using: foreigncolumn = tables.Column(accessor='foreignmodel.foreigncolumnname'), I only see a '--'...
# The models:
class Organism(models.Model):
species_name = models.CharField(max_length=200)
strain_name = models.CharField(max_length=200)
eukaryotic = models.BooleanField(default=True)
lipids = models.ManyToManyField('Lipid',blank=True)
class Lipid(models.Model):
lm_id = models.CharField(max_length=100)
common_name = models.CharField(max_length=100,blank=True)
category = models.CharField(max_length=100,blank=True)
#The tables
class OrganismTable(tables.Table):
name = tables.LinkColumn('catalog:organism-detail', text=lambda record: record.species_name, args=[A('pk')])
lp = tables.Column(accessor='Lipid.common_name')
class Meta:
model = Organism
sequence = ['name','lp']
exclude = ['id','species_name']
Any idea what I'm doing wrong?
This does not work so easily for ManyToManyFields because of the simple way Accessor works. You could display the repr of the related QuerySet via 'lipids.all' but that does not seem sufficient here. You can, however, add a property (or method) to your Organism model and use it in the accessor. This way, you can display any custom information related to the instance:
class Organism(models.Model):
# ...
#property
def lipid_names(self):
return ', '.join(l.common_name for l in self.lipids.all()) # or similar
class OrganismTable(tables.Table):
# ...
lp = tables.Column(accessor='lipid_names')
I would recommend then to add a prefetch_related('lipids') to the Organism QuerySet that you pass to the table for better performance.
How do you get a field related by OneToOneField and ManyToManyField in Django?
For example,
class A(models.Model):
myfield = models.CharField()
as = models.ManyToManyField('self')
class B(models.Model):
a = models.OneToOneField(A)
If I want to get a 'myfield' and all associated 'as' using class B, given a 'myfield' equal to a string like 'example', how is it done?
Models.py
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
def __str__(self): # __unicode__ on Python 2
return "%s the place" % self.name
class Restaurant(models.Model):
place = models.OneToOneField(
Place,
on_delete=models.CASCADE,
primary_key=True,
)
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False)
def __str__(self): # __unicode__ on Python 2
return "%s the restaurant" % self.place.name
Let create a place instance.
p1 = Place.objects.create(name='Demon Dogs', address='944 W. Fullerton')
Then create a restaurant object.
r = Restaurant.objects.create(place=p1, serves_hot_dogs=True, serves_pizza=False)
Now, to access place from Restaurant:
>>> r.place
<Place: Demon Dogs the place>
vice-versa to access Restaurant from place
>>> p1.restaurant
<Restaurant: Demon Dogs the restaurant>
I did not understand the many-to-many field part can you please elaborate?
First you get an instance of B say b and you can easily access myfield and as through the a attribute of b
b.a.myfield
b.a.as.all()
Furthermore, CharField requires a max_length attribute as follows:
class A(models.Model):
myfield = models.CharField(max_length=128)
as = models.ManyToManyField('self')
class B(models.Model):
a = models.OneToOneField(A)
A general point, give more descriptive names to your models and their attributes, or at the very least, add comments explaining what these models represent
Let's say I have 3 models in Django: Person, Workfield and SubWorkfield.
A person can have many workfield-s and many subWorkfield-s as well, but the subWorkfield-s must be related to their parent workfield-s ( which the person must be related to).
So how do I enforce that whenever a person is related to a subWorkfield then he must also be related to that subWorkfield's parent workfield?
Here's what I have so far, but I don't think it enforces the relationship:
class Person(models.Model):
name = models.CharField(max_length=200)
workfield = models.ManyToManyField(Workfield)
subworkfield = models.ManyToManyField(SubWorkfield)
class Workfield(models.Model):
name = models.CharField(max_length=200)
class SubWorkfield(models.Model):
name = models.CharField(max_length=200)
workfield = models.ForeignKey(Workfield)
I need to have the workfields and subWorkfield-s decoupled because a person can belong to a workfield without any subWorkfield-s.
There really isn't any good way to do what you want purely with the table structure itself. However, if you do the following:
class Person(models.Model):
name = models.CharField(max_length=200)
workfields = models.ManyToManyField(Workfield)
subworkfields = models.ManyToManyField(SubWorkfield)
def add_subworkfield(self, subworkfield):
if subworkfield.workfield not in self.workfields:
return False
else:
self.subworkfields.append(subworkfield)
class Workfield(models.Model):
name = models.CharField(max_length=200)
class SubWorkfield(models.Model):
name = models.CharField(max_length=200)
workfield = models.ForeignKey(Workfield)
And then you will just use the add_subworkfield method when adding subworkfields to Persons.
Alternatively, inside of the Person model you could override the save method:
class Person(models.Model):
...
def save(self, *args, **kwargs):
for subworkfield in self.subworkfields:
if subworkfield.workfield not in self.workfields:
return
super(Person, self).save(*args, **kwargs)
If it's a small app and it's just you, the first way should suffice. However, if you're working on something larger with multiple people, overriding save would be a safety incase someone doesn't use the add_subworkfield method.