Django UniqueConstraint with expressions fails - django

I am trying and failing to create a UniqueConstraint for a model combining a TruncDate expression of a datetime field and a couple of other fields following the approach here:
https://docs.djangoproject.com/en/4.1/ref/models/constraints/#expressions
The idea is that each User can only create one Point per day for a Definition
This will work if I just use the TruncDate expression, or if I don't use the expression and create a constraint using fields = ("bucket", "user", "definition") but fails if I try to combine the expression with other fields. Database is Postgresql. Can anyone shed any light?
models.py
class Point(models.Model):
definition = models.ForeignKey(Definition, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
bucket = models.DateTimeField(default=timezone.now)
value = models.DecimalField(decimal_places=5, max_digits=10)
class Meta:
ordering = ["-bucket", "definition"]
constraints = [
UniqueConstraint(
TruncDate("bucket"),
"user",
"definition",
name="max_daily_frequency",
)
]
test.py
class TestPoint(TestCase)
def test_make_point(self)
u = User.objects.create()
d = Definition.objects.create()
p = Point.objects.create(user=u, definition=d, value=Decimal(1))
assert p.pk
This fails with the following:
/usr/local/lib/python3.10/site-packages/factory/base.py:40: in __call__
return cls.create(**kwargs)
/usr/local/lib/python3.10/site-packages/factory/base.py:528: in create
return cls._generate(enums.CREATE_STRATEGY, kwargs)
/usr/local/lib/python3.10/site-packages/factory/django.py:117: in _generate
return super()._generate(strategy, params)
/usr/local/lib/python3.10/site-packages/factory/base.py:465: in _generate
return step.build()
/usr/local/lib/python3.10/site-packages/factory/builder.py:262: in build
instance = self.factory_meta.instantiate(
/usr/local/lib/python3.10/site-packages/factory/base.py:317: in instantiate
return self.factory._create(model, *args, **kwargs)
/usr/local/lib/python3.10/site-packages/factory/django.py:166: in _create
return manager.create(*args, **kwargs)
/usr/local/lib/python3.10/site-packages/django/db/models/manager.py:85: in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
/usr/local/lib/python3.10/site-packages/django/db/models/query.py:671: in create
obj.save(force_insert=True, using=self.db)
db/models.py:214: in save
self.full_clean()
/usr/local/lib/python3.10/site-packages/django/db/models/base.py:1491: in full_clean
self.validate_constraints(exclude=exclude)
/usr/local/lib/python3.10/site-packages/django/db/models/base.py:1442: in validate_constraints
constraint.validate(model_class, self, exclude=exclude, using=using)
/usr/local/lib/python3.10/site-packages/django/db/models/constraints.py:335: in validate
expressions = [
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
.0 = <tuple_iterator object at 0x7f8fb78e1f60>
expressions = [
> Exact(expr, expr.replace_references(replacement_map))
for expr in self.expressions
]
E AttributeError: 'F' object has no attribute 'replace_references'
/usr/local/lib/python3.10/site-packages/django/db/models/constraints.py:336: AttributeError

Related

How to insert list value in dictionary for default_get method Odoo11

I am using default_get method for one2many fields and I can see fields in the list but when I want to pass these fields in the dictionary by the super method in show blank dictionary. So, how to get this?
My python code is here:
#api.model
def default_get(self, fields):
print("FIELDS", fields)
rec = super(CrmContactLine, self).default_get(fields)
print("REC", rec)
context = dict(self._context or {})
partner = self.env['res.partner'].browse(context['partner_id'])
fields['partner_m2m'] = partner.child_ids
return rec
GOT RESULT ON TERMINAL 'FIELDS' SHOW LIST VALUES BUT 'REC' DICTIONARY NULL
FIELDS ['name', 'email', 'designation', 'linkedln_profile', 'indentifier', 'approached_status', 'approach_date', 'email_status', 'follow_up_date', 'partner_m2m']
REC {}
Thanks in advance.
I got a solution to this problem.
#api.model
def default_get(self, fields):
rec = super(CrmContactLine, self).default_get(fields)
context = dict(self._context or {})
partner = self.env['res.partner'].browse(context['partner_id'])
rec['partner_m2m'] = [(6, 0, partner.child_ids.ids)] # if your field type is many2many
return rec
If the super call to the method default_get return an empty dict that it's because there will be no default values defined for that model, and that's a valid result. You need to put your own values before return the result dict rec in your case, not in the fields list. This should work for you
#api.model
def default_get(self, fields):
rec = super(CrmContactLine, self).default_get(fields)
context = dict(self._context or {})
partner = self.env['res.partner'].browse(context['partner_id'])
rec['partner_m2m'] = partner.child_ids
return rec

Database for Pytest Flask application does not work correctly

I got a flask app and trying to test it with pytest. The question is how to make the base create a new one for each test function? This is how I think it should work.
This is my models.
models.py
class User(UserMixin, db.Model): # type: ignore
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
password_hash = db.Column(db.String(128))
def set_password(self, password: str) -> None:
self.password_hash = generate_password_hash(password) # type: ignore
def check_password(self, password) -> None:
return check_password_hash(self.password_hash, password) # type:ignore
class Employee(db.Model): # type: ignore
query_class = EmployeeSearch
__tablename__ = 'employee'
id = db.Column(db.Integer, primary_key=True)
first_name = db.Column(db.String(60))
patronymic = db.Column(db.String(60))
last_name = db.Column(db.String(60))
position = db.Column(db.String(60))
office = db.Column(db.String)
birthday = db.Column(db.Date)
sex = db.Column(db.String)
I am trying to drop session after each function and remove changes.
conftest.py
import os
import pytest
from backend.auth import db as _db
from backend.auth import create_app
from backend.auth.models import User
class TestConfig(object):
TESTING = True
WTF_CSRF_ENABLED = False
SECRET_KEY = os.environ.get('SECRET_KEY', 'JustaSecret')
SQLALCHEMY_DATABASE_URI = 'postgresql://postgres:admin#localhost/FlaskTest'
SQLALCHEMY_TRACK_MODIFICATIONS = False
DEBUG = True
#pytest.yield_fixture(scope='session')
def app(request):
flask_app = create_app(TestConfig)
# Flask provides a way to test your application by exposing the Werkzeug test Client
# and handling the context locals for you.
testing_client = flask_app.test_client()
# Establish an application context before running the tests.
ctx = flask_app.app_context()
ctx.push()
def teardown():
ctx.pop()
request.addfinalizer(teardown)
return testing_client
#pytest.fixture(scope='session')
def db(app, request):
# Create the database and the database table
def teardown():
_db.drop_all()
_db.app = app
_db.create_all()
user = User(username='admin')
user.set_password('12345')
_db.session.add(user)
_db.session.commit()
# Commit the changes for the users
request.addfinalizer(teardown)
return _db
#pytest.fixture(scope='function')
def session(db, request):
"""Creates a new database session for a test."""
# connect to the database
connection = db.engine.connect()
# begin a non-ORM transaction
transaction = connection.begin()
# bind an individual session to the connection
options = dict(bind=connection, binds={})
session = db.create_scoped_session(options=options)
# overload the default session with the session above
db.session = session
def teardown():
session.close()
# rollback - everything that happened with the
# session above (including calls to commit())
# is rolled back.
transaction.rollback()
# return connection to the Engine
connection.close()
session.remove()
request.addfinalizer(teardown)
return session
But if I try to call session multiple times.
test_employee.py
class TestEmployee:
def test_create_employee_get(self, app, session):
app.post('/login', data=dict(username='admin', password='12345'),
content_type='application/x-www-form-urlencoded', follow_redirects=True)
response = app.get('/create')
assert response.status_code == 200
assert b'Create Employee' in response.data
assert b'first name' in response.data
assert b'Date of birth' in response.data
assert b'sex' in response.data
def test_create_employee_post(self, app, session):
app.post('/login', data=dict(username='admin', password='12345'),
content_type='application/x-www-form-urlencoded', follow_redirects=True)
response = app.post('/create', data=dict(
first_name='Another', patronymic='Test', last_name='Just',
position='Check', office='Москва', birthday='1990-01-01', sex='man'),
content_type='application/x-www-form-urlencoded', follow_redirects=True)
assert response.status_code == 200
It doesn't drop database after each function.
____________________________________________________________________ ERROR at setup of TestEmployee.test_create_employee_get ____________________________________________________________________
[gw2] darwin -- Python 3.6.6 /Users/vladimir/UsetechTelegramBot/venv/bin/python
self = <sqlalchemy.engine.base.Connection object at 0x106737b70>
dialect = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x10671ac50>
constructor = <bound method DefaultExecutionContext._init_compiled of <class 'sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2'>>
statement = 'INSERT INTO "user" (username, password_hash) VALUES (%(username)s, %(password_hash)s) RETURNING "user".id'
parameters = {'password_hash': 'pbkdf2:sha256:50000$Ek5K9otp$eea50131bd908bfb87427fe975f0f04e2e6607dd002919da77d1ad322a95078d', 'username': 'admin'}
args = (<sqlalchemy.dialects.postgresql.psycopg2.PGCompiler_psycopg2 object at 0x106737860>, [{'password_hash': 'pbkdf2:sha256:50000$Ek5K9otp$eea50131bd908bfb87427fe975f0f04e2e6607dd002919da77d1ad322a95078d', 'username': 'admin'}])
conn = <sqlalchemy.pool._ConnectionFairy object at 0x10677b4a8>
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x106737898>
def _execute_context(self, dialect, constructor,
statement, parameters,
*args):
"""Create an :class:`.ExecutionContext` and execute, returning
a :class:`.ResultProxy`."""
try:
try:
conn = self.__connection
except AttributeError:
# escape "except AttributeError" before revalidating
# to prevent misleading stacktraces in Py3K
conn = None
if conn is None:
conn = self._revalidate_connection()
context = constructor(dialect, self, conn, *args)
except BaseException as e:
self._handle_dbapi_exception(
e,
util.text_type(statement), parameters,
None, None)
if context.compiled:
context.pre_exec()
cursor, statement, parameters = context.cursor, \
context.statement, \
context.parameters
if not context.executemany:
parameters = parameters[0]
if self._has_events or self.engine._has_events:
for fn in self.dispatch.before_cursor_execute:
statement, parameters = \
fn(self, cursor, statement, parameters,
context, context.executemany)
if self._echo:
self.engine.logger.info(statement)
self.engine.logger.info(
"%r",
sql_util._repr_params(parameters, batches=10)
)
evt_handled = False
try:
if context.executemany:
if self.dialect._has_events:
for fn in self.dialect.dispatch.do_executemany:
if fn(cursor, statement, parameters, context):
evt_handled = True
break
if not evt_handled:
self.dialect.do_executemany(
cursor,
statement,
parameters,
context)
elif not parameters and context.no_parameters:
if self.dialect._has_events:
for fn in self.dialect.dispatch.do_execute_no_params:
if fn(cursor, statement, context):
evt_handled = True
break
if not evt_handled:
self.dialect.do_execute_no_params(
cursor,
statement,
context)
else:
if self.dialect._has_events:
for fn in self.dialect.dispatch.do_execute:
if fn(cursor, statement, parameters, context):
evt_handled = True
break
if not evt_handled:
self.dialect.do_execute(
cursor,
statement,
parameters,
> context)
venv/lib/python3.6/site-packages/sqlalchemy/engine/base.py:1193:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x10671ac50>
cursor = <cursor object at 0x1065deed0; closed: -1>
statement = 'INSERT INTO "user" (username, password_hash) VALUES (%(username)s, %(password_hash)s) RETURNING "user".id'
parameters = {'password_hash': 'pbkdf2:sha256:50000$Ek5K9otp$eea50131bd908bfb87427fe975f0f04e2e6607dd002919da77d1ad322a95078d', 'username': 'admin'}
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x106737898>
def do_execute(self, cursor, statement, parameters, context=None):
> cursor.execute(statement, parameters)
E psycopg2.IntegrityError: duplicate key value violates unique constraint "ix_user_username"
E DETAIL: Key (username)=(admin) already exists.
venv/lib/python3.6/site-packages/sqlalchemy/engine/default.py:509: IntegrityError
The above exception was the direct cause of the following exception:
app = <FlaskClient <Flask 'backend.auth'>>
request = <SubRequest 'db' for <Function 'test_create_employee_get'>>
#pytest.fixture(scope='session')
def db(app, request):
# Create the database and the database table
def teardown():
_db.drop_all()
_db.app = app
_db.create_all()
user = User(username='admin')
user.set_password('12345')
_db.session.add(user)
> _db.session.commit()
tests/conftest.py:64:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
venv/lib/python3.6/site-packages/sqlalchemy/orm/scoping.py:153: in do
return getattr(self.registry(), name)(*args, **kwargs)
venv/lib/python3.6/site-packages/sqlalchemy/orm/session.py:943: in commit
self.transaction.commit()
venv/lib/python3.6/site-packages/sqlalchemy/orm/session.py:467: in commit
self._prepare_impl()
venv/lib/python3.6/site-packages/sqlalchemy/orm/session.py:447: in _prepare_impl
self.session.flush()
venv/lib/python3.6/site-packages/sqlalchemy/orm/session.py:2254: in flush
self._flush(objects)
venv/lib/python3.6/site-packages/sqlalchemy/orm/session.py:2380: in _flush
transaction.rollback(_capture_exception=True)
venv/lib/python3.6/site-packages/sqlalchemy/util/langhelpers.py:66: in __exit__
compat.reraise(exc_type, exc_value, exc_tb)
venv/lib/python3.6/site-packages/sqlalchemy/util/compat.py:249: in reraise
raise value
venv/lib/python3.6/site-packages/sqlalchemy/orm/session.py:2344: in _flush
flush_context.execute()
venv/lib/python3.6/site-packages/sqlalchemy/orm/unitofwork.py:391: in execute
rec.execute(self)
venv/lib/python3.6/site-packages/sqlalchemy/orm/unitofwork.py:556: in execute
uow
venv/lib/python3.6/site-packages/sqlalchemy/orm/persistence.py:181: in save_obj
mapper, table, insert)
venv/lib/python3.6/site-packages/sqlalchemy/orm/persistence.py:866: in _emit_insert_statements
execute(statement, params)
venv/lib/python3.6/site-packages/sqlalchemy/engine/base.py:948: in execute
return meth(self, multiparams, params)
venv/lib/python3.6/site-packages/sqlalchemy/sql/elements.py:269: in _execute_on_connection
return connection._execute_clauseelement(self, multiparams, params)
venv/lib/python3.6/site-packages/sqlalchemy/engine/base.py:1060: in _execute_clauseelement
compiled_sql, distilled_params
venv/lib/python3.6/site-packages/sqlalchemy/engine/base.py:1200: in _execute_context
context)
venv/lib/python3.6/site-packages/sqlalchemy/engine/base.py:1413: in _handle_dbapi_exception
exc_info
venv/lib/python3.6/site-packages/sqlalchemy/util/compat.py:265: in raise_from_cause
reraise(type(exception), exception, tb=exc_tb, cause=cause)
venv/lib/python3.6/site-packages/sqlalchemy/util/compat.py:248: in reraise
raise value.with_traceback(tb)
venv/lib/python3.6/site-packages/sqlalchemy/engine/base.py:1193: in _execute_context
context)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <sqlalchemy.dialects.postgresql.psycopg2.PGDialect_psycopg2 object at 0x10671ac50>
cursor = <cursor object at 0x1065deed0; closed: -1>
statement = 'INSERT INTO "user" (username, password_hash) VALUES (%(username)s, %(password_hash)s) RETURNING "user".id'
parameters = {'password_hash': 'pbkdf2:sha256:50000$Ek5K9otp$eea50131bd908bfb87427fe975f0f04e2e6607dd002919da77d1ad322a95078d', 'username': 'admin'}
context = <sqlalchemy.dialects.postgresql.psycopg2.PGExecutionContext_psycopg2 object at 0x106737898>
def do_execute(self, cursor, statement, parameters, context=None):
> cursor.execute(statement, parameters)
E sqlalchemy.exc.IntegrityError: (psycopg2.IntegrityError) duplicate key value violates unique constraint "ix_user_username"
E DETAIL: Key (username)=(admin) already exists.
E [SQL: 'INSERT INTO "user" (username, password_hash) VALUES (%(username)s, %(password_hash)s) RETURNING "user".id'] [parameters: {'username': 'admin', 'password_hash': 'pbkdf2:sha256:50000$Ek5K9otp$eea50131bd908bfb87427fe975f0f04e2e6607dd002919da77d1ad322a95078d'}] (Background on this error at: http://sqlalche.me/e/gkpj)
venv/lib/python3.6/site-packages/sqlalchemy/engine/default.py:509: IntegrityError
Changed to this and all works fine.
#pytest.fixture(scope='session')
def db(app):
_db.app = app
_db.create_all()
return _db

Django testing - TypeError: int() argument must be a string, a bytes-like object or a number, not 'User'

I'm writing test cases for my first Django application and using mixer to generate random values for some modules.
Test case written for testing on the models is like
test_model.py
from datetime import datetime, timedelta
from django.core.exceptions import ValidationError
from tzlocal import get_localzone
import pytest
from django.test import TestCase
from mixer.backend.django import mixer
from transactions.models import ModeOfPayment, AmountGiven
pytestmark = pytest.mark.django_db
#pytest.mark.django_db
class TestAmountReturned(TestCase):
def test_model_amount_return_add(self):
amount_returned = mixer.blend(
'transactions.AmountReturned',
amount_given=mixer.blend(
'transactions.AmountGiven',
given_date=datetime.now(get_localzone()) - timedelta(days=300)
),
amount=100.00,
return_date=datetime.now(get_localzone()) - timedelta(days=50)
)
assert str(amount_returned) == str(amount_returned.amount), '__str__ should return amount string'
def test_model_amount_due(self):
amount = 10000.00
interest_rate = 9.5
duration = 365
given_date = datetime.now(get_localzone()) - timedelta(days=200)
returned_amount = 150.00
amount_given = mixer.blend(
'transactions.AmountGiven',
contact=mixer.blend('contacts.Contact'),
amount=amount,
interest_rate=interest_rate,
duration=duration,
given_date=given_date,
mode_of_payment=mixer.blend('transactions.ModeOfPayment', title='Cash')
)
mixer.blend(
'transactions.AmountReturned',
amount_given=amount_given,
amount=returned_amount,
return_date=datetime.now(get_localzone()) - timedelta(days=50)
)
assert amount_given.amount_due == amount_given.total_payable - returned_amount, 'Should return dues amount'
But on running the testing
pipenv run py.test
It is giving the following error
______________________________ TestAmountReturned.test_model_amount_due _______________________
self = <django.db.models.fields.AutoField: id>, value = <User: khess>
def to_python(self, value):
if value is None:
return value
try:
> return int(value)
E TypeError: int() argument must be a string, a bytes-like object or a number, not 'User'
../../../.local/share/virtualenvs/koober-py-McGChbzt/lib/python3.6/site-packages/django/db/models/fields/__init__.py:940: TypeError
During handling of the above exception, another exception occurred:
self = <transactions.tests.test_models.TestAmountReturned testMethod=test_model_amount_due>
def test_model_amount_due(self):
amount = 10000.00
interest_rate = 9.5
duration = 365
given_date = datetime.now(get_localzone()) - timedelta(days=200)
returned_amount = 150.00
amount_given = mixer.blend(
'transactions.AmountGiven',
> contact=mixer.blend('contacts.Contact'),
amount=amount,
interest_rate=interest_rate,
duration=duration,
given_date=given_date,
mode_of_payment=mixer.blend('transactions.ModeOfPayment', title='Cash')
)
src/transactions/tests/test_models.py:182:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
location of the error in my test case is pointing to
contact=mixer.blend('contacts.Contact'),
but could not figure out which column this error is occurring for. I have used the mixer to blend contacts with other locations also, few of them are working fine.

Odoo no UserError message without any error

My code:
from openerp.tools.translate import _
from openerp.exceptions import UserError
and function:
#api.multi
def button_in_progress(self):
for rec in self:
rec.state = 'in_progress'
test = self.test_ids.ids
test1 = len(test)
if test1 == 0:
raise UserError(_('Test test'))
return True
I logged. When I get test1 is 0, my error message doesn't appear. Also I don't get any of errors. What can be wrong?
No need to find len and then check just try this
#api.multi
def button_in_progress(self):
for rec in self:
rec.state = 'in_progress'
if not self.test_ids.ids:
raise UserError(_('Test test'))
return True

How can override default_get in odoo9?

I want to override default_get in odoo9 and My code
class account_payment(models.Model):
_inherit = "account.payment"
#api.model
def default_get(self, fields):
#print 'PRINT1',MAP_INVOICE_TYPE_PARTNER_TYPE
rec = super(account_payment, self).default_get(fields)
invoice_defaults = self.resolve_2many_commands('invoice_ids', rec.get('invoice_ids'))
if invoice_defaults and len(invoice_defaults) == 1:
invoice = invoice_defaults[0]
rec['communication'] = invoice['reference'] or invoice['name'] or invoice['number']
rec['currency_id'] = invoice['currency_id'][0]
rec['payment_type'] = invoice['type'] in ('out_invoice', 'in_refund', 'sponsor_invoice',) and 'inbound' or 'outbound' # modified for charity
rec['partner_type'] = MAP_INVOICE_TYPE_PARTNER_TYPE[invoice['type']]
rec['partner_id'] = invoice['partner_id'][0]
rec['amount'] = invoice['residual']
return rec
but when I click create it shows an error message, that point the inherited class first then calling base class, how is that possible? Please help.
Error message:
File "/opt/odoo_v9_charity/server/addons/web_charity/models/account_payment.py", line 87, in default_get
rec = super(account_payment_charity, self).default_get(fields)
File "/opt/odoo_v9_charity/server/openerp/api.py", line 248, in wrapper
return new_api(self, *args, **kwargs)
File "/opt/odoo_v9_charity/server/openerp/addons/account/models/account_payment.py", line 257, in default_get
rec['partner_type'] = MAP_INVOICE_TYPE_PARTNER_TYPE[invoice['type']]
KeyError: u'sponsor_out_invoice'
The dict/map MAP_INVOICE_TYPE_PARTNER_TYPE simply does not have the key 'sponsor_out_invoice'. Maybe you should add it.