Choosing from a list of names using factory boy integrated with faker - django-rest-framework

I am trying to use factory.faker to randomly choose from a list of four companies and use them as a traffic source for a list of generated names. I am using the below code:
from django.db import models
import factory
import factory.django
from datetime import datetime
from django.core.validators import MinValueValidator, MaxValueValidator
from faker import Faker
from faker.providers import BaseProvider
import random
fake = Faker()
class User(models.Model):
name = models.CharField(max_length=64)
address = models.CharField(max_length=128)
phone_number = models.CharField(max_length=32)
login_date = models.DateTimeField(default=datetime.now(), blank=True)
session_duration = models.IntegerField(default = 0, validators= [
MinValueValidator(0),
MaxValueValidator(5)
])
traffic_source = models.CharField(max_length=32)
class UserFactory(factory.django.DjangoModelFactory):
class Meta:
model = User
name = factory.Faker('name')
address = factory.Faker('address')
phone_number = factory.Faker('phone_number')
login_date = factory.Faker('date')
session_duration = factory.Faker('random_int')
traffic_source = random.choice(['XYZ', 'ABC', '123', '456'])
The issue is that for all 200 iterations I perform using the following in the python shell:
for _ in range(200):
UserFactory.create()
I get the same company for every name, i.e. 'XYZ' for all 200 names.
Am I missing something? I want to get a different company for each of the 200 iterations. Any help is much appreciated. Thank you!

This comes from Python's parsing rules.
Why?
When you write this:
class UserFactory(factory.django.DjangoModelFactory):
...
traffic_source = random.choice(['XYZ', 'ABC', '123', '456'])
Python will execute the following steps:
Read the class declaration body;
Reach the line traffic_source = random.choice(['XYZ', 'ABC', '123', '456']);
Evaluate the call to random.choice, which might return 'ABC';
Once each line of the class body has been read (and its function calls evaluated), create the class:
UserFactory = type(
name='UserFactory',
bases=[factory.django.DjangoModelFactory],
{'traffic_source': 'ABC', ...},
)```
As you can see, the call to random.choice is performed only once, when parsing the class declaration.
This is, basically, the reason for all the factory.XXX declarations: they yield an object that will only execute its specific rules when building an instance from the factory.
So, what should you do?
Here, you should use:
Either factory.Faker using Faker's random_choices provider;
Or factory.fuzzy.FuzzyChoice:
class UserFactory(factory.django.DjangoModelFactory):
...
traffic_source = factory.Faker('random_choices', elements=['XYZ', 'ABC', '123', '456'])
alt_traffic_source = factory.fuzzy.FuzzyChoice(['XYZ', 'ABC', '123', '456'])
The main difference between factory.Faker('random_choices') and factory.fuzzy.FuzzyChoices is that factory.fuzzy.FuzzyChoices supports lazily evaluating generators; this is useful if you want to choose from a queryset:
factory.Faker('random_choices', elements=Company.objects.all()) will perform a DB query at import time;
factory.fuzzy.FuzzyChoice(Company.objects.all()) will only query the DB the first time UserFactory.create() is called.

While not truly random, the effect you're looking for when choosing from among a set of pre-existing records can also be achieved by using FactoryBoy's Iterator, which can also work with a QuerySet. For example, here I wanted every object to be created by someone different from the set of existing fake users:
from django.contrib.auth import get_user_model
...
# Then, within a factory class, for one of the fields:
created_by = factory.Iterator(get_user_model().objects.all())

Related

SqlAlchemy query filter for Enum class property of Column(Enum(...))

I have a declarative class that has an Enum column, and the Enum has a property that returns True/False based on the specific enumerated name or value. It would simplify life if I could do a query with a filter based on this property, such as the following (see implementation below):
session.query(MyTable).filter(MyTable.letter.is_vowel)
using something like the below straightforward attempt at an expression fails with
AttributeError: Neither 'InstrumentedAttribute' object nor 'Comparator' object associated with MyTable.letter has an attribute 'is_vowel'
The below implementation is too simple to allow for construction of the necessary query. Is there a way to do this? I thought maybe something in a Comparator might work, or maybe there's something more sophisticated that would do it?
import enum
from sqlalchemy import (
Column,
Enum,
Integer,
)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.hybrid import hybrid_property
MyDeclarativeBase = declarative_base()
class Letter(enum.Enum):
A = 1
B = 2
C = 3
D = 4
E = 5
# and so on...
#property
def is_vowel(self):
return self.name in 'AEIOU'
class MyTable(MyDeclarativeBase):
__tablename__ = 'my_table'
id = Column(Integer, primary_key=True, autoincrement=True)
letter = Column(Enum(Letter), nullable=False)
#hybrid_property
def is_vowel(self):
"""Return True if the row's letter is a vowel."""
return self.letter.is_vowel
#is_vowel.expression
def is_vowel(cls):
return cls.letter.is_vowel

use generic types in python to refactor code

I have been trying several ways to refactor the following code as these classes are recurring in my app:
class CreateRecord(Mutation):
record = Field(lambda: Unit)
class Arguments:
input = CreateInput(required=True)
def mutate(self, info, input):
data = input_to_dictionary(input)
data['createdAt'] = datetime.utcnow()
# data['createdBy'] = <user> # TODO: <user> input
record = UnitModel(**data)
db_session.add(record)
db_session.commit()
return CreateRecord(record=record)
class UpdateRecord(Mutation):
record = Field(lambda: Unit)
class Arguments:
input = UpdateInput(required=True)
def mutate(self, info, input):
data = input_to_dictionary(input)
data['updatedAt'] = datetime.utcnow()
# data['updatedBy'] = <user> # TODO: <user> input
record = db_session.query(UnitModel).filter_by(id=data['id'])
record.update(data)
db_session.commit()
record = db_session.query(UnitModel).filter_by(id=data['id']).first()
return UpdateRecord(record=record)
class DeleteRecord(Mutation):
record = Field(lambda: Unit)
class Arguments:
input = DeleteInput(required=True)
def mutate(self, info, input):
data = input_to_dictionary(input)
data['deletedAt'] = datetime.utcnow()
# data['deletedBy'] = <user> # TODO: <user> input
data['isDeleted'] = True
record = db_session.query(UnitModel).filter_by(id=data['id'])
record.update(data)
db_session.commit()
record = db_session.query(UnitModel).filter_by(id=data['id']).first()
return DeleteRecord(record=record)
I was thinking of using generic types but I'm kinda' stuck on how to implement it. I've tried creating a master class and in the
def mutate:
method I'd just check if it's a create, update or delete action but I still want to work with generic types before I do that.
Any help is highly appreciated. TIA.
I've solved this particular problem for myself with a mixin class that includes the following method:
from graphene.utils.str_converters import to_snake_case
class MutationResponseMixin(object):
#classmethod
def get_operation_type(cls):
"""
Determine the CRUD type from the mutation class name.
Uses mutation's class name to determine correct operation.
( create / update / delete )
"""
return to_snake_case(cls.__name__).split('_')[0]
This allows me to include a mutation method in the mixin that is shared by create, update, and delete methods and takes conditional action based on value of get_operation_type.
I also needed a way to determine the base record from the mixin's mutation (which in your case would be UnitModel) so my case I ended up declarding it explicitly as an attribute of each mutation class.

Model validation values in custom Django form error messages

I have created a Django form:
class AddFaceForm(forms.ModelForm):
class Meta:
model = Face
fields = ('person', 'x', 'y', 'w', 'h')
In the Face model, w is defined as
w = models.DecimalField(
'Relative width',
validators=[MinValueValidator(0.05), MaxValueValidator(1)]
)
If a user enters 0 for the width, the error message Django returns upon form validation is
Ensure this value is greater than or equal to 0.05.
So it nicely contains the value from the MinValueValidator. I would like to change it to "Relative width must be >= 0.05".
To achieve it, I tried customising errors in AddFaceForm Meta
class AddFaceForm(forms.ModelForm):
class Meta:
model = Face
fields = ('person', 'x', 'y', 'w', 'h')
error_messages = {
'w': {
'min_value': 'Relative width must be >= 0.05'
}
}
But this means hard-coding the 0.05 value. I would like to read it from the model definition. How can this be done?
Great question.
MinValueValidator is a subclass of BaseValidator. And, BaseValidator takes an optional message in its constructor. From the code you can see that the message can use the following three parameters: limit_value, show_value and value. limit_value is the minimum value for the validator which is 0.05 in this case. show_value is the cleaned value the user enters and value is the raw value they enter. Hence, you can do the following to achieve your goal:
from django.core.validators import MinValueValidator
from django.db import models
from django.utils.translation import ugettext_lazy as _
class Face(models.Model):
w = models.DecimalField(
'Relative width',
max_digits=5,
decimal_places=2,
validators=[MinValueValidator(0.05, _('Relative width must be >= %(limit_value)s'))],
)
Alternatively, if you'd rather specify the error message in a form then you can do the following:
from django import forms
from django.utils.translation import ugettext_lazy as _
from .models import Face
class FaceForm(forms.ModelForm):
class Meta:
model = Face
fields = ['w']
error_messages = {
'w': {
'min_value': _('Relative width must be >= %(limit_value)s')
}
}
The DecimalField docs tell you that you can interpolate limit_value.

How can I create a complete_name field in a custom module for a custom hierarchy like used on product categories in Odoo?

I'm trying to create a field “complete_name” that displays a hierarchy name similar to whats done on the product categories grid but I can't seem to get it to work. It just puts Odoo in an endless loading screen when I access the relevant view using the new field "complete_name".
I have tried to copy the code used in addons/product/product.py and migrate to work with Odoo 9 API by using compute instead of .function type but it did not work.
Can someone help me understand whats wrong? Below is my model class which works fine without the complete_name field in my view.
class cb_public_catalog_category( models.Model ):
_name = "cb.public.catalog.category"
_parent_store = True
parent_left = newFields.Integer( index = True )
parent_right = newFields.Integer( index = True )
name = newFields.Char( string = 'Category Name' )
child_id = newFields.One2many( 'catalog.category', 'parent_id', string = 'Child Categories' )
complete_name = newFields.Char( compute = '_name_get_fnc', string = 'Name' )
def _name_get_fnc( self ):
res = self.name_get( self )
return dict( res )
Your compute function is supposed to define the value of an attribute of your class, not return a value. Ensure the value you are assigning complete_name is a string.
Also name_get() returns a tuple. I am not sure if you really want a string representation of this tuple or just the actual name value.
Try this
def _name_get_fnc( self ):
self.complete_name = self.name_get()[1]
If you really want what is returned by name_get() then try this.
def _name_get_fnc( self ):
self.complete_name = str(self.name_get())
If you are still having issues I would incorporate some logging to get a better idea of what you are setting the value of complete_name to.
import logging
_logger = logging.getLogger(__name__)
def _name_get_fnc( self ):
_logger.info("COMPUTING COMPLETE NAME")
_logger.info("COMPLETE NAME: " + str(self.name_get()))
self.complete_name = self.name_get()
If this does not make it apparent what the issue is you could always try statically assigning it a value in the off chance that there is a problem with your view.
def _name_get_fnc( self ):
self.complete_name = "TEST COMPLETE NAME"
After further review I think I have the answer to my own question. It turns out as with a lot of things its very simple.
Simply use "_inherit" and inherit the product.category
model. This gives access to all the functions and fields
of product.category including the complete_name field
and computes the name from my custom model data. I was
able to remove my _name_get_func and just use the inherited
function.
The final model definition is below. Once this
update was complete I was able to add a "complete_name" field
to my view and the results were as desired!
class cb_public_catalog_category( models.Model ):
_name = "cb.public.catalog.category"
_inherit = 'product.category'
_parent_store = True
parent_left = newFields.Integer( index = True )
parent_right = newFields.Integer( index = True )
name = newFields.Char( string = 'Category Name' )
child_id = newFields.One2many( 'catalog.category', 'parent_id', string = 'Child Categories' )

Django rest framework serialize queryset of many models

I would like to combine 2 querysets from 2 differents models, then I need to order them by date and finally my goal is to serialize it.
So far I did that :
last_actions = serializers.SerializerMethodField()
def get_last_actions(self, obj):
prc = obj.product_request_configs.all().order_by('modified_date')[:5]
psc = obj.product_send_configs.all().order_by('modified_date')[:5]
result_list = sorted(
chain(prc, psc),
key=attrgetter('modified_date'),
reverse=True)
But I don't know how to call my two django rest serializers so that I can return the right data.
If I could make a database view it coult be simpler I think.
Serializers are designed for match one model relationship, so we need to create a custom Model for the logic you are trying to achieve:
class CustomModel(models.Model):
def dictfetchall(self, cursor):
"""Returns all rows from a cursor as a dict"""
desc = cursor.description
return [dict(zip([col[0] for col in desc], row))
for row in cursor.fetchall()]
def yourMethod(self):
cursor = connection.cursor()
cursor.execute("""
select field1, field2 from app_table
where field1=%s and field2=%s group by field1
""",
[value1, value2,]
)
return self.dictfetchall(cursor)
class Meta:
abstract = True
This will return a dictionary and then you can serialize that response with a seializer like:
class CustomModelSerializer(serializers.Serializer):
field1 = serializers.IntegerField()
field2 = serializers.CharField()
Please note that on SQL you can use as keyword to rename some fields, the current name of fields must match var names in your serializer.

Resources