Как я могу обновить переменную класса, вызвав метод класса из производного класса

Я разрабатываю пакет для тестирования под названием dbtest. Этот пакет связан с тем, что я использую MySQLdb для подключения баз данных, и, следовательно, очень утомительно писать запросы sql во время тестирования. Поэтому я создал новый пакет, и ко всем запросам можно получить доступ с помощью отдельных функций. Я избегал django ORM, потому что в моей таблице базы данных есть несколько внешних и первичных ключей.

Ниже представлено часть пакета.

пакет.py

from django.test import TestCase
dbcon='connector'
class testcase(TestCase):
    flag_user=[]

@classmethod                                                               
def setUpClass(cls):

    global dbcon
    dbcon=MySQLdb.connect(host=dbHost,port=dbPort,user=dbUser,passwd=dbPasswd,db=dbname)
    super(testcase, cls).setUpClass()

    cursor = dbcon.cursor()
    sql=open("empty.sql").read()
    cursor.execute(sql)
    cursor.close()

    views.MySQLdb=Mockdb()

@classmethod
def tearDownClass(cls):
   dbcon.close()

def user_table(self,username=username,email=email):

    cache=[username]
    self.flag_user.append(cache)
    cmpdata=(username,email)
    insert_table(tablename_user,cmpdata)

def delete(self,table):
    last_entry=self.flag_user[-1]
    query_user = 'delete from USER where USERNAME=%s'
    cursor=dbcon.cursor()
    query=eval('query_%s'%table)
    cursor.execute(query,last_entry)
    dbcon.commit()
    del self.flag_user[-1]

тесты.py

from package import testcase
class showfiles(testcase):

    def setUp(self):
      print "setup2"
      self.user_table(username='vishnu',email='[email protected]')

    def tearDown(self):
      print "teardown2"
      self.delete("user")

    def test_1(self):
      print "test dbtest link feature"


    def test_2(self):
      print "test health/errorfiles with valid device"
      self.user_table(username='vishnu',email='[email protected]')

Insert_table в пакете выполняет операцию вставки в sql, а метод удаления удаляет последнюю запись от пользователя. empty.sql создает таблицы для базы данных. На самом деле, когда я запускаю тесты, наконец, flag_user должен содержать только [['vishnu']]. Но я получаю [['vishnu'],['vishnu']], и это потому, что функция удаления при разборке не обновляет значение.

Я думаю, это связано с экземплярами класса? Прав я или нет?


person vishnu m c    schedule 25.09.2017    source источник
comment
Может быть, я что-то упустил, но почему ваши методы в package.py не имеют отступа внутри класса? Кроме того, можете ли вы показать нам функцию delete_table?   -  person Ben    schedule 25.09.2017
comment
delete_table - это не что иное, как SQL-запрос cursor=dbcon.cursor() query="delete from user where username=%s" cursor.execute(query,'vishnu') dbcon.commit() cursor.close()   -  person vishnu m c    schedule 25.09.2017
comment
@Бен, пожалуйста, посмотри на это   -  person vishnu m c    schedule 25.09.2017
comment
Откуда tablename в delete и user_table? (И уж точно не следует использовать eval в delete_table.)   -  person Daniel Roseman    schedule 25.09.2017
comment
Накладные расходы Django ORM никогда не были проблемой ни в одном из десятков проектов django, над которыми я работал за последние десять лет (при условии, что вы, конечно, научитесь правильно его использовать, но это просто здравый смысл). Вы действительно заботились о том, чтобы профилировать что-либо, прежде чем решить, что это должно быть медленным?   -  person bruno desthuilliers    schedule 25.09.2017
comment
Да. Я просто подтверждаю самый быстрый метод, сравнивая время, затрачиваемое как на django ORM, так и на MySQLdb для одного и того же доступа. @brunodesthuilliers Не могли бы вы предложить мне решение вышеуказанной проблемы?   -  person vishnu m c    schedule 25.09.2017
comment
@brunodeshuilliers основная причина в том, что я хочу переопределить структуру таблицы, если использую модели django. Я использую несколько первичных ключей, а также внешние ключи. Пожалуйста, поймите мою проблему   -  person vishnu m c    schedule 25.09.2017
comment
В конце концов, это довольно близкая копия stackoverflow.com/questions/19753897/. В self.flag_user=[] вы присваиваете переменной экземпляра flag_user пустой список вместо изменения переменной класса. Используйте self.flag_user.clear(), например.   -  person Ilja Everilä    schedule 26.09.2017
comment
@IljaEverilä Поскольку это объект списка, мы не можем использовать clear()   -  person vishnu m c    schedule 26.09.2017
comment
Нет, это потому, что вы используете устаревшую версию Python. Если у вас нет веских причин не делать этого, вам следует использовать Python 3.   -  person Ilja Everilä    schedule 26.09.2017
comment
Извините, я использую Python 2.7 @IljaEverilä   -  person vishnu m c    schedule 26.09.2017
comment
Я работаю с другим приложением django, оно написано на python 2.7. Поэтому я вынужден использовать python 2.7 @IljaEverilä   -  person vishnu m c    schedule 26.09.2017
comment
del self.flag_user[:] будет эквивалентом Py2. Но уверены ли вы, что вам нужны переменные класса для начала?   -  person Ilja Everilä    schedule 26.09.2017
comment
@vishnumc Я не говорил, что у ORM нет накладных расходов, я сказал, что по моему опыту (используя Django с момента первого публичного выпуска) эти накладные расходы никогда не были проблемой - узкие места (выявляемые с помощью надлежащего профилировщика) всегда где либо из-за неправильного использования ORM (т. е. извлечение всех моделей, когда требуется только одно поле, неправильное использование select_related и/или prefetch_related и т. д.), либо из-за неправильного определения схемы (отсутствующие индексы, индексы, которые недостаточно дискриминантны, 255 символов полей, когда используется только 10 и т. д.).   -  person bruno desthuilliers    schedule 26.09.2017
comment
@vishnumc теперь, конечно, если у вас есть устаревшая база данных с составными ключами и вы не можете изменить схему, это действительно является веской причиной для обхода формы, но вы все равно можете использовать соединение формы, управление транзакциями и т. д. cf docs.djangoproject.com/en/1.11/topics/db/sql /   -  person bruno desthuilliers    schedule 26.09.2017
comment
@IljaEverilä проблема OP не имеет ничего общего с версией Python, и есть очень веские причины по-прежнему использовать Python 2.7.x - вы можете не знать, но большая часть работы по разработке связана с поддержкой устаревших проектов и переносом большого проекта на Python 3 только ради этого ваши заинтересованные стороны не будут платить за это.   -  person bruno desthuilliers    schedule 26.09.2017
comment
@brunodeshuilliers list отсутствие предложенного метода во многом связано с версией Python, которая обсуждалась в тот конкретный момент. Я хорошо осведомлен о проблемах с унаследованным кодом, спасибо, но обычно следует побуждать кажущихся новичков переходить на него — на самом деле это лучше для них в долгосрочной перспективе. Особенно, когда в самом вопросе было указано 0, что это для существующей устаревшей кодовой базы. Это всплыло постфактум.   -  person Ilja Everilä    schedule 26.09.2017
comment
@brunodesthuilliers, какая часть, если у вас нет очень веской причины не делать этого, вам неясна, и как то, что вы заявили в какой-либо части, делает недействительным то, что сказал Илья? Python 2 настроен на окончание срока действия — хотя могут быть текущие обязательства по Python 2, само собой разумеется, что больше не нужно.   -  person Antti Haapala    schedule 26.09.2017


Ответы (1)


Здесь :

class testcase(TestCase):
    flag_user=[]

вы создаете flag_user как атрибут класса (общий для всех экземпляров).

Тогда здесь:

def user_table(self,username=username,email=email):
    cache=[username]
    self.flag_user.append(cache)

Вы добавляете к атрибуту flag_user (уровень класса) (доступ к нему осуществляется через экземпляр, но он по-прежнему является атрибутом класса)

Но здесь:

def delete(self,table):
    delete_table(tablename)
    self.flag_user=[]

вы создаете атрибут flag_user для самого экземпляра, который полностью отделен от атрибута класса эпонима.

Самое простое решение — использовать атрибут экземпляра с самого начала вместо использования атрибута класса:

# package.py

from django.test import TestCase
dbcon='connector'

class testcase(TestCase):
    def setUp(self): 
        self.flag_user = []

и не забудьте вызвать testcase.setUp в дочерних классах:

# tests.py

from package import testcase
class showfiles(testcase):

    def setUp(self):
      super(showfile, self).setUp()
      self.user_table(username='vishnu',email='[email protected]')

Альтернативное решение, если вам действительно нужен атрибут класса (я не могу представить, зачем вам это нужно, но...), состоит в том, чтобы изменить testcase.delete(), чтобы он действительно очищал атрибут класса flag_user вместо создания атрибута экземпляра, что делается путем явного запроса python для повторной привязки атрибута к самому классу (type(obj) возвращает obj.__class__, который является классом, к которому принадлежит экземпляр):

def delete(self,table):
    delete_table(tablename)
    type(self).flag_user = []
person bruno desthuilliers    schedule 26.09.2017
comment
Или лучше во всех методах использовать testcase.flag_user, не так ли? - person vishnu m c; 26.09.2017
comment
Конечно, мне нужны атрибуты класса. Насколько мне известно, каждый метод в showfiles() имеет self, который, в свою очередь, ссылается на класс testcase, а каждый self в методах showfiles() представляет собой разные экземпляры testcase. Мои знания верны или нет? - person vishnu m c; 26.09.2017
comment
В противном случае лучше использовать testcase.flag_user во всех методах => не задавайте имя класса жестко, вместо этого используйте type(self).flag_user. И действительно, было бы более явно использовать это везде, но самое простое решение - по-прежнему использовать вместо этого атрибут экземпляра. - person bruno desthuilliers; 26.09.2017
comment
Поэтому каждый раз, когда я вызываю self.user_table() в отдельных методах showfile(), self отличается, и, следовательно, они относятся к отдельным экземплярам тестового примера. - person vishnu m c; 26.09.2017
comment
Да self — это текущий экземпляр. Но поскольку вы вызываете delete("user") в методе tearDown, он будет вызываться для каждого теста, поэтому делать flag_user атрибутом класса просто бесполезно. Для чего вы используете этот атрибут на самом деле? - person bruno desthuilliers; 26.09.2017
comment
flag_user используется для отслеживания записи пользовательской таблицы. Он используется в delete() для доступа к последней записи пользователя. delete() удалить только последнюю запись. - person vishnu m c; 26.09.2017
comment
Поэтому, когда я удаляю запись, мне также нужно стереть трек - person vishnu m c; 26.09.2017
comment
Он используется в delete() для доступа к последней записи в user =› where ??? Это не то, что в вашем посте. - person bruno desthuilliers; 26.09.2017
comment
Извините, я удалил это, чтобы уменьшить код для публикации. Я отредактирую свой вопрос, если вы хотите, чтобы я - person vishnu m c; 26.09.2017
comment
Не могли бы вы взглянуть на функцию удаления сейчас? Есть ли лучший способ сделать это? - person vishnu m c; 26.09.2017
comment
Давайте продолжим это обсуждение в чате. - person vishnu m c; 26.09.2017