I've got a Purchase model and a PurchaseInfo model. PurchaseInfo has a foreign key to Purchase. I'm trying to modify an existing Factory for Purchase that will create PurchaseInfo at the same time using RelatedFactory since it's a reverse foreign key relationship. The only thing is that I wanted to use a Taint so that I could control the value of fields within PurchaseInfo. Normally when I create a Purchase like p = PurchaseFactory() the PurchaseInfo is created with null fields inside of it. If I create a Purchase like p = PurchaseFactory(info=True), so I can get the field modifications via the Taint, the PurchaseInfo is not created at all.
I have a feeling that putting the RelatedFactory in a Taint is not the way to go. What is the correct way to do this?
Models:
class Purchase(Model):
...
class PurchaseInfo(Model):
purchase = models.ForeignKey(Purchase, on_delete=models.CASCADE, unique=True, db_index=True)
lock = DateTimeField(null=True)
lock_by = ForeignKey(...
class PurchaseInfoFactory(DjangoModelFactory):
class Meta:
model = PurchaseInfoField
lock = None
lock_by = None
class PurchaseFactory(DjangoModelFactory):
class Meta:
model = Purchase
info = RelatedFactory(PurchaseInfoFactory,
factory_related_name='purchase')
class Params:
info = Trait(internalfield=RelatedFactory(PurchaseInfoFactory,
factory_related_name='purchase',
lock=timezone.now() - relativedelta(months=1),
lock_by=SubFactory(UserFactory, user_id=1)))
I don't like my solution, but here's what I did. I derived a secondary factory with an alternate RelatedFactory. That works.
class PurchaseFactory(DjangoModelFactory):
class Meta:
model = Purchase
info = RelatedFactory(PurchaseInfoFactory,
factory_related_name='purchase')
class InfoPurchaseFactory(DjangoModelFactory):
class Meta:
model = Purchase
info = Trait(internalfield=RelatedFactory(PurchaseInfoFactory,
factory_related_name='purchase',
lock=timezone.now() - relativedelta(months=1),
lock_by=SubFactory(UserFactory, user_id=1)))
Related
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.
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.
I've been looking at the docs for search_fields in django admin in the attempt to allow searching of related fields.
So, here are some of my models.
# models.py
class Team(models.Model):
name = models.CharField(max_length=255)
class AgeGroup(models.Model):
group = models.CharField(max_length=255)
class Runner(models.Model):
"""
Model for the runner holding a course record.
"""
name = models.CharField(max_length=100)
agegroup = models.ForeignKey(AgeGroup)
team = models.ForeignKey(Team, blank=True, null=True)
class Result(models.Model):
"""
Model for the results of records.
"""
runner = models.ForeignKey(Runner)
year = models.IntegerField(_("Year"))
time = models.CharField(_("Time"), max_length=8)
class YearRecord(models.Model):
"""
Model for storing the course records of a year.
"""
result = models.ForeignKey(Result)
year = models.IntegerField()
What I'd like is for the YearRecord admin to be able to search for the team which a runner belongs to. However as soon as I attempt to add the Runner FK relationship to the search fields I get an error on searches; TypeError: Related Field got invalid lookup: icontains
So, here is the admin setup where I'd like to be able to search through the relationships. I'm sure this matches the docs, but am I misunderstanding something here? Can this be resolved & the result__runner be extended to the team field of the Runner model?
# admin.py
class YearRecordAdmin(admin.ModelAdmin):
model = YearRecord
list_display = ('result', 'get_agegroup', 'get_team', 'year')
search_fields = ['result__runner', 'year']
def get_team(self, obj):
return obj.result.runner.team
get_team.short_description = _("Team")
def get_agegroup(self, obj):
return obj.result.runner.agegroup
get_agegroup.short_description = _("Age group")
The documentation reads:
These fields should be some kind of text field, such as CharField or TextField.
so you should use 'result__runner__team__name'.
I have troubles updating attributes that are inherited from other tables
class AgentCategory(models.Model):
""" Agent Category """
class Meta:
verbose_name_plural = "agentcategories"
name = models.CharField(max_length=200, unique=True)
description = models.TextField(blank=True)
class Agent(models.Model):
agentcategory = models.ManyToManyField(AgentCategory,null=True)
How should i go about manually updating agentcategory in Agent model? As of now i am trying out this method, however, it does not work):
property_selected.agentcategory = "api/v1/agentcategory/3"
property_selected.save()
Any ideas? Thanks!
As Agent has ManyToManyField relation with AgentCategory.
agentcategory would contain the list of entries.
you can update its entries by,
agent_cats = AgentCategory.objects.filter(...)
property_selected.agentcategory.clear()
property_selected.agentcategory = agent_cats
property_selected.agentcategory.save()