NullableForeignKey

I am still using Django 1.1, so I am still using a NullableForeignKey to override the default ForeignKey model to get around the cascade on delete when I need to.

(at the end is my NullableForeignKey that I am using)

What I can't figure out is how to convince pyCharm that the methods on the NullableForeignKey objects are actually there.  example:

class Foo(models.Model):

    something = models.CharField(max_length=20)

    def getSomethingSpecial(self):

         return self.something + ' special'

class Bar(models.Model):

     myFoo = NullableForeignKey(Foo)

     def someFunction(self):

          myVar =  myFoo.getSomethingSpecial()

                 ^^^^^^How do I get pyCharm to understand that this is OK (and know that myFoo.badFuncName() is a problem?

class NullableForeignKey(models.ForeignKey):

    """

    Just like a ForeignKey, but when related objects are deleted this object is

    *not* deleted. As the name implies, this field is always NULLable.

    """

    def __init__(self, *args, **kwargs):

        kwargs['null'] = kwargs['blank'] = True

        super(NullableForeignKey, self).__init__(*args, **kwargs)

    # Monkey patches the related classes ``_collect_sub_objects`` method

    # to NULL out values in this field that point to the object that's about

    # to be deleted.

    # NOTE:  Override _collect_sub_objects rather than delete so a custom delete

    # method can still be used.

    def contribute_to_related_class(self, cls, related):

        super(NullableForeignKey, self).contribute_to_related_class(cls, related)

        # Alias so that the closure below has access to the outer "self"

        this_field = self

        # define a method name to map the original `_collect_sub_objects` method to

        _original_csb_attr_name = '_original_collect_sub_objects'

        def _new_collect_sub_objects(self, *args, **kwargs):

            # NULL out anything related to this object.

            qn = connection.ops.quote_name

            for related in self._meta.get_all_related_objects():

                if isinstance(related.field, this_field.__class__):

                    table = qn(related.model._meta.db_table)

                    column = qn(related.field.column)

                    sql = "UPDATE %s SET %s = NULL WHERE %s = %%s;" % (table, column, column)

                    connection.cursor().execute(sql, [self.pk])

            # Now proceed with collecting sub objects that are still tied via FK

            getattr(self, _original_csb_attr_name)(*args, **kwargs)

        # monkey patch the related classes _collect_sub_objects method.

        # store the original method in an attr named `_original_csb_attr_name`

        if not hasattr(cls, _original_csb_attr_name):

            setattr(cls, _original_csb_attr_name, cls._collect_sub_objects)

            setattr(cls, '_collect_sub_objects', _new_collect_sub_objects)

1 comment
Comment actions Permalink

Hello Rusty,

At the moment PyCharm treats only fields of ForeignKey class as foreign keys;

it doesn't check fields of classes that inherit from ForeignKey. You're welcome

to file an issue for that problem in YouTrack: http://youtrack.jetbrains.net/

I am still using Django 1.1, so I am still using a NullableForeignKey

to override the default ForeignKey model to get around the cascade on

delete when I need to.

(at the end is my NullableForeignKey that I am using)

What I can't figure out is how to convince pyCharm that the methods on

the NullableForeignKey objects are actually there.  example:

class Foo(models.Model):

something = models.CharField(max_length=20)

def getSomethingSpecial(self):

return self.something + ' special'

class Bar(models.Model):

myFoo = NullableForeignKey(Foo)

def someFunction(self):

myVar =  myFoo.getSomethingSpecial()

^^^^^^How do I get pyCharm to understand that this is

OK (and know that myFoo.badFuncName() is a problem?

class NullableForeignKey(models.ForeignKey):

"""

Just like a ForeignKey, but when related objects are deleted this

object is

not deleted. As the name implies, this field is always NULLable.

"""

def __init__(self, *args, **kwargs):

kwargs['null'] = kwargs['blank'] = True

super(NullableForeignKey, self).__init__(*args, **kwargs)

  1. Monkey patches the related classes ``_collect_sub_objects``

method

  1. to NULL out values in this field that point to the object that's

about

  1. to be deleted.

  2. NOTE:  Override collectsub_objects rather than delete so a

custom delete

  1. method can still be used.

def contribute_to_related_class(self, cls, related):

super(NullableForeignKey,

self).contribute_to_related_class(cls, related)

  1. Alias so that the closure below has access to the outer

"self"

this_field = self

  1. define a method name to map the original

`_collect_sub_objects` method to

originalcsb_attr_name = '_original_collect_sub_objects'

def newcollect_sub_objects(self, *args, **kwargs):

  1. NULL out anything related to this object.

qn = connection.ops.quote_name

for related in self._meta.get_all_related_objects():

if isinstance(related.field, this_field.__class__):

table = qn(related.model._meta.db_table)

column = qn(related.field.column)

sql = "UPDATE %s SET %s = NULL WHERE %s = %%s;" %

(table, column, column)

connection.cursor().execute(sql, )

  1. Now proceed with collecting sub objects that are still

tied via FK

getattr(self, originalcsb_attr_name)(*args, **kwargs)

  1. monkey patch the related classes collectsub_objects

method.

  1. store the original method in an attr named

`_original_csb_attr_name`

if not hasattr(cls, originalcsb_attr_name):

setattr(cls, originalcsb_attr_name,

cls._collect_sub_objects)

setattr(cls, '_collect_sub_objects',

newcollect_sub_objects)

---

Original message URL:

http://devnet.jetbrains.net/message/5297599#5297599

--

Dmitry Jemerov

Development Lead

JetBrains, Inc.

http://www.jetbrains.com/

"Develop with Pleasure!"

0

Please sign in to leave a comment.