Issue: Disable backref validation
Author: coleifer
State: closed
Created: 2014-11-18 23:26:46

Via [stackoverflow](http://stackoverflow.com/questions/27000279/peewee-reusing-dynamically-created-models):

> In my application I have a model for which I know the number of columns only at runtime. Using a function like Factory below to create the model solves this issue nicely. However, if I use it multiple times (with potentially varying fields), the creation of the foreign key ref throws an exception:
> 
> ``` python
> AttributeError: Foreign key: dynamictable.ref related name "dynamictable_set" 
> collision with foreign key using same related_name.
> ```
> 
> The message is quite clear and when I set the related_name argument when creating the foreign key there is no error.
> 
> Questions:
> 
> Why can't I use the same related_name the second time? Do I need to redefine StaticTable as well?
> 
> Is there a better approach to write to multiple databases with dynamic models?

Minimal, reproducable example:

``` python
import peewee

database_proxy = peewee.Proxy()

class BaseModel(peewee.Model):
    class Meta:
        database = database_proxy

class StaticTable(BaseModel):
    foo = peewee.DoubleField()

def Factory(fields):
    class DynamicTable(BaseModel):
        ref = peewee.ForeignKeyField(StaticTable)
    for field in fields:
        peewee.DoubleField().add_to_class(DynamicTable, field)
    return DynamicTable 

def Test(fname, fields):
    db = peewee.SqliteDatabase(fname)
    database_proxy.initialize(db)
    db.create_table(StaticTable)
    dyntable = Factory(fields)
    db.create_table(dyntable)
    db.close()


Test(':memory:', ['foo', 'bar'])
Test(':memory:', ['foo', 'bar', 'extra'])
```


I noticed an indentation error in the minimal example above: `return DynamicTable` is indented one level to deep.

I must confess that I have not read the peewee source in detail. But could it be, that even after the deletion of `dyntable`, the class `StaticTable` still has some sort of reference to it?


I was able to make it work by defining destructors on the ForeingKeyField and the BaseModel classes:
https://github.com/hitzg/peewee/commit/3cb1feb4f477d540d6ff1ad951c04304eee176a7 
I did not define a new test, but all the old ones seem to run ok. I can create a pull request, but I'm not sure if my solution is safe / not introducing other problems.

EDIT: haha, I haven't seen your commit.... sorry


Actually I think I made the indentation error when copying the code from StackOverflow.

Even after the deleteion of `dyntable`, yes, `Statictable` will retain a back-reference. Maybe I need to find a way to hook into the deletion of a Model class from memory.


Ah, you added `__del__` to the model metaclass, perfect! I wlil definitely take your changes, they look great.


Actually quick question -- in your code on `BaseModel.__del__` your override accepts the `self` argument, yet it accesses `cls` inside. I'm curious how this worked, since I would think that would be a `NameError`.


Sorry, I really was too quick on that. you are quite right, using `cls` raises a `NameError`. Not sure what I have tested before, but somehow I tricked myself.
I figured out two things:
1. The approach works (at least with these corrections: https://github.com/hitzg/peewee/commit/f6cafe05ecbd43b48b9acf23c4f4ad400e00c72d),  as far as I can tell (Its +3000 lines of code and I haven't spent a lot of time looking at it ;)
2. The destructors need to be called explicitly (in the minimal example above you have to call do `dyntable.__del__()`, which means that somewhere there are further references to the objects. I'm not sure where though. So it might make more sense to put the code in some sort of `release_backrefs` function.


> The destructors need to be called explicitly (in the minimal example above you have to call do `dyntable.__del__()`, which means that somewhere there are further references to the objects. I'm not sure where though. So it might make more sense to put the code in some sort of release_backrefs function.

Ahh, that makes sense. I was testing locally and there were still references hanging around. Even calling `del SomeClass` did not work. I started a SO thread which has more details: http://stackoverflow.com/questions/27018721/python-del-method-for-classes


At this point I will have to use the fix you provided. Thank you for all the help!
One more question: Is there a downside / performance loss related to setting `validate_backrefs = True`?


Not really, just that you may overwrite a pre-existing backref or attribute. In your case I think it should be fine.