3

I'm plugging Django into an existing system. I have been given a legacy schema, and I have to work with it.

I have generated models with inspectdb, but I'm getting hundreds of:

ABC>DEF: Accessor for field 'HIJ' clashes with related field 'KLM'. Add a related_name argument to the definition for 'HIJ'.

I wouldn't have a problem with this if it were only a small number. But it isn't.

Is there any automatic way to fix this by adding unique related_names?

EDIT: For clarification, I do am looking for an automatic solution to the problem, not one that involves editing each field by hand.

Joe
  • 46,419
  • 33
  • 155
  • 245

4 Answers4

4

Occasionally I find myself in this situation. Modifying inspectdb has worked for me in several instances but I can't promise it'll be appropriate in every situation. Here's what I've done:

    #Original inspectdb
    121                         if rel_to in known_models:
    122                             field_type = 'ForeignKey(%s' % rel_to
    123                         else:
    124                             field_type = "ForeignKey('%s'" % rel_to

    #Modified inspectdb
    121                         if rel_to in known_models:
    122                             field_type = 'ForeignKey(%s' % rel_to
    123                         else:
    124                             field_type = "ForeignKey('%s'" % rel_to
    125                         extra_params['related_name'] = '%s_%s' % (table2model(table_name),column_to_field_name[column_name])

This will automatically add a related_name argument to every generated model that isn't likely to collide with any others. Again, it depends on the situation, but this single line modification has saved me tons of work.

musashiXXX
  • 4,192
  • 4
  • 22
  • 24
  • 2
    Thanks for your answer. This was 7 years ago and I have long since left this problem, and context behind. – Joe Oct 18 '18 at 15:21
  • 2
    @Joe Sure thing! I do realize it's a 7 year old conversation :-) I just stumbled across this thread when researching a similar issue and figured somebody else might benefit from what I had to say. – musashiXXX Oct 18 '18 at 17:08
  • 1
    I was tempted to wait another 7 years before replying... Anyway, thanks for your answer. Hope others find it useful. – Joe Oct 23 '18 at 13:54
  • 1
    I'm coming from the future and I have to say thanks! This should be the accepted answer. @musashiXXX if you're not going to, can I submit a PR to the django repository on your behalf? – Sebastián Vansteenkiste Jul 31 '19 at 16:13
  • @SebastiánVansteenkiste I don't think this is quite PR-level, but I certainly have found it to be a useful hack. – musashiXXX Aug 19 '19 at 21:02
  • Well, @musashiXXX I actually tried but got shot down because of this ticket: https://code.djangoproject.com/ticket/11179 – Sebastián Vansteenkiste Aug 20 '19 at 12:58
  • @SebastiánVansteenkiste That's an old one... the closing comment kind of sounds like a cop-out but whatever. – musashiXXX Aug 21 '19 at 14:32
  • Yeah, I'm still thinking this should be part of the default behaviour but I guess they'd rather not touch the codebase? – Sebastián Vansteenkiste Aug 22 '19 at 14:35
  • I feel like something like this should make it into django as an option on inspectdb. Maybe somebody can add a PR? – D2TheC Aug 23 '21 at 09:19
2

If you don't need the related name, you can set it to "+".

If you don't want to disable them completely or cannot use "+" in your django version (I can't remember when it was added), you may use something like the following:

_no_related_name_counter = 0
def no_related_name():
    _no_related_name_counter += 1
    return "_norn_%%(class)s_%i" % _no_related_name_counter

ForeignKey(..., related_name=no_related_name())

But if you insert related names with a regexp search and replace, you may as well set them to something readable, e.g. replace (\s*(\w+)\s*=\s*models\.ForeignKey\(.*)\)(\s*)$ with $1, related_name="%(class)s_$2_reverse")$3

emulbreh
  • 3,421
  • 23
  • 27
  • Yeah, I have ended up doing textual processing on the source file to add "+". I had hoped there might be something less hacky I could do. – Joe Mar 15 '11 at 23:12
1

You should check the foreign key relationships by specifying the related_name, like this:

user = models.ForeignKey(User, related_name='user')
admin = models.ForeignKey(User, related_name='user1') #you need to use a different related_name here

Hope it helps.

zs2020
  • 53,766
  • 29
  • 154
  • 219
  • Thanks for your answer, but that's not feasible for hundreds of tables. Sorry, I don't think I can be clearer with my question. – Joe Mar 15 '11 at 19:35
  • Take a look at this thread then: http://stackoverflow.com/questions/583327/django-model-with-2-foreign-keys-from-the-same-table – zs2020 Mar 15 '11 at 20:02
0

If you want to deal with large legacy database and dont want to use '+' you can run a simple script to modify the generated models.py file as follows:

import fileinput
textToSearch = "models.DO_NOTHING"

_no_related_name_counter = 1000

with fileinput.FileInput('models.py', inplace=True, backup='.bak') as file:
    for line in file:
        _no_related_name_counter += 1
        textToReplace = 'models.DO_NOTHING, related_name="%(class)s_{}"'.format(_no_related_name_counter)
        print(line.replace(textToSearch, textToReplace), end='')