-2

I am developing a package for my testing purpose called dbtest. This package is because i am using MySQLdb for connecting databases and hence it is very tedious task to write sql queries while testing. So i created a new package and all queries can be accessed with separate functions. I avoided django ORM because my database table have multiple foreign keys and primary keys.

Below present is a part of the package.

package.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]

tests.py

from package import testcase
class showfiles(testcase):

    def setUp(self):
      print "setup2"
      self.user_table(username='vishnu',email='vishnu@clartrum.com')

    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='vishnu@clartrum.com')

The insert_table in package execute insert operation in sql and delete method deletes the last entry from user. empty.sql creates tables for the database. Actually when i run the tests, finally the flag_user should contain only [['vishnu']]. But i get [['vishnu'],['vishnu']] and this is because delete function in teardown doesn't updating the value.

I think this is due to class instances ? Am i right or not?

vishnu m c
  • 841
  • 1
  • 7
  • 21
  • Maybe I'm missing something but why aren't your methods in package.py indented to be within the class? Also, can you show us the delete_table function? – Ben Sep 25 '17 at 18:02
  • delete_table is nothing but a sql query `cursor=dbcon.cursor() query="delete from user where username=%s" cursor.execute(query,'vishnu') dbcon.commit() cursor.close()` – vishnu m c Sep 25 '17 at 18:12
  • @Ben please look into that – vishnu m c Sep 25 '17 at 18:17
  • 1
    Where is `tablename` coming from in `delete` and `user_table`? (And you certainly shouldn't be using `eval` in `delete_table`.) – Daniel Roseman Sep 25 '17 at 18:36
  • 2
    Django ORM overhead has never been an issue in any of the dozens django projects I worked on in the past ten years (assuming you learn to use it properly of course but that's just plain common sense). Did you actually care profiling anything before deciding it was "to slow"? – bruno desthuilliers Sep 25 '17 at 18:38
  • yes i do. I just confirm the fastest method by comparing time taken for both django ORM and MySQLdb for same access. @brunodesthuilliers Can you please suggest me a solution for the above problem? – vishnu m c Sep 25 '17 at 18:43
  • @brunodesthuilliers the main reason is i want to override my table structure if i am using django models. I have using multiple primary key as well as foriegn keys. Please understand my problem – vishnu m c Sep 25 '17 at 18:50
  • In the end this is a pretty close duplicate of https://stackoverflow.com/questions/19753897/difference-between-class-variables-and-instance-variables. In `self.flag_user=[]` you assign to an **instance variable** *flag_user* an empty list instead of mutating the **class variable**. Use `self.flag_user.clear()` for example. – Ilja Everilä Sep 26 '17 at 07:01
  • @IljaEverilä Since it is a list object we can't use clear() – vishnu m c Sep 26 '17 at 07:49
  • No, that's because you're using an obsolescent version of Python. Unless you have some very good reason not to, you should be using Python 3. – Ilja Everilä Sep 26 '17 at 08:14
  • Sorry, I am using python 2.7 @IljaEverilä – vishnu m c Sep 26 '17 at 08:32
  • I am working with a another django application it is written in python 2.7. So i forced to use python 2.7 @IljaEverilä – vishnu m c Sep 26 '17 at 08:34
  • `del self.flag_user[:]` would be the Py2 equivalent. But are you sure you need class variables to begin with? – Ilja Everilä Sep 26 '17 at 08:43
  • @vishnumc I didn't say the ORM didn't have any overhead, I said that in my experience (been using Django since the first public release), this overhead has never been an issue - bottlenecks (identified with a proper profiler) always where due to either a wrong usage of the ORM (ie fetching the whole models when only one field is needed, not properly using `select_related` and/or `prefetch_related` etc) or a wrong schema definition (missing indexes, indexes that not discriminant enough, 255 chars fields when only 10 are used, etc). – bruno desthuilliers Sep 26 '17 at 10:08
  • @vishnumc now of course if you have a legacy database with compound keys and can't alter the schema that's indeed a valid reason to bypass the orm - but you can still use the orm's connection, transaction management etc cf https://docs.djangoproject.com/en/1.11/topics/db/sql/#executing-custom-sql-directly – bruno desthuilliers Sep 26 '17 at 10:10
  • @IljaEverilä the OP problem has nothing to do with Python version, and there are very valid reasons to still use Python 2.7.x - you might not know but most of the development work is about maintaining legacy projects, and porting a large project to Python 3 just for the sake of it is not something your stakeholders are going to pay for. – bruno desthuilliers Sep 26 '17 at 10:14
  • @brunodesthuilliers `list` not having the proposed method has everything to do with Python version, which was being discussed at that particular point. I'm well aware about the pains of legacy code, thank you, but usually one should encourage seemingly newcomers to make the switch – it's actually best for them in the long run. Especially when the question itself had 0 indication that it's for an existing legacy code base. That came up after the fact. – Ilja Everilä Sep 26 '17 at 10:25
  • @brunodesthuilliers which part of "unless you have some very good reason not to" is unclear to you and how that which you stated in any part invalidates what Ilja said? Python 2 is set to be EOLed - while there might be current commitments to Python 2, it should go without saying that **no more** should be made. – Antti Haapala -- Слава Україні Sep 26 '17 at 10:33

1 Answers1

1

Here :

class testcase(TestCase):
    flag_user=[]

you create flag_user as a class attribute (shared by all instances).

Then here:

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

You append to the (class level) flag_user attribute (it's accessed thru the instance but it's still the class attribute)

But here:

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

you create a flag_user attribute on the instance itself, which is totally disconnected from the eponym class attribute.

The simplest solution is to use an instance attribute right from the start instead of using a class attribute:

# package.py

from django.test import TestCase
dbcon='connector'

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

and don't forget to call testcase.setUp in child classes:

# tests.py

from package import testcase
class showfiles(testcase):

    def setUp(self):
      super(showfile, self).setUp()
      self.user_table(username='vishnu',email='vishnu@clartrum.com')

The alternative solution if you really want a class attribute (I can't imagine why you would but...) is to modify testcase.delete() so it really clears the flag_user class attribute instead of creating an instance attribute, which is done by explicitely asking python to rebind the attribute on the class itself (type(obj) returns obj.__class__ which is the class the instance belongs to):

def delete(self,table):
    delete_table(tablename)
    type(self).flag_user = []
bruno desthuilliers
  • 75,974
  • 6
  • 88
  • 118
  • Or otherwise it is better to use testcase.flag_user in all methods, isn't it? – vishnu m c Sep 26 '17 at 10:23
  • Of course, i need class attributes. Up to my knowledge, each method in showfiles() have a `self` which in turns refer to testcase class and each `self` in showfiles() methods are different instances of testcase. My knowledge is correct or not? – vishnu m c Sep 26 '17 at 10:28
  • "Or otherwise it is better to use testcase.flag_user in all methods" => don't hardcode the class name, use `type(self).flag_user` instead. And well it would indeed be more explicit to use this everywhere but the simplest solution is _still_ to use an instance attribute instead. – bruno desthuilliers Sep 26 '17 at 10:31
  • So each time when i call `self.user_table()` in separate methods of showfile(), the `self` is different and hence they refer to separate instances of testcase – vishnu m c Sep 26 '17 at 10:31
  • 1
    Yes `self` is the current instance. But since you call `delete("user")` in the `tearDown` method, it will be called for each test, so making `flag_user` a class attribute is just plain useless. What are you using this attribute for actually ? – bruno desthuilliers Sep 26 '17 at 10:35
  • 1
    `flag_user` used to keep the track of user table entry. It is used in `delete()` for accessing the last entry in user. `delete()` only delete the last entry. – vishnu m c Sep 26 '17 at 10:39
  • 1
    So when i delete an entry and i have to erase the track also – vishnu m c Sep 26 '17 at 10:40
  • "It is used in delete() for accessing the last entry in user" => where ??? That's not what's in your post. – bruno desthuilliers Sep 26 '17 at 10:47
  • Sorry, I eliminated it to reduce the code to post. I will edit my question if you want me to – vishnu m c Sep 26 '17 at 10:51
  • Can you please look into the delete function now? Is there a better a way to do this? – vishnu m c Sep 26 '17 at 10:58
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/155350/discussion-between-vishnu-m-c-and-bruno-desthuilliers). – vishnu m c Sep 26 '17 at 15:51