Field, that specified twice in foreign keys - django

I have following model.
class Comment(models.Model):
type = models.CharField(max_length=21, choices=OBJECT_TYPE_CHOICES)
program = models.ForeignKey(Program, db_column='object_id', to_field='id', null=True, blank=True)
article = models.ForeignKey(Article, db_column='object_id', to_field='id', null=True, blank=True)
Type field determine, which field (program or article) will be active. But when i try to add comment using Django admin panel, I get error: "Column 'object_id' specified twice". I understand why this error occurs, but don't understand how to fix it.

This type of behavior isn't supported by Django. Even if you managed to accomplish it, it's a dirty, dirty hack and will result in much cursing at you by any developer that should ever be so unfortunate as to inherit your code.
Use the contenttypes framework, specifically GenericForeignKeys: https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/#generic-relations

The problem is because you're using the same name for two columns in database, I think you should use this:
https://docs.djangoproject.com/en/1.3/ref/contrib/contenttypes/

Related

Deprecating fields in django model

I'm normalizing a database associated with a Django project and will be moving fields to different tables. As part of the implementation process, I'd like to throw a deprecation warning to my colleagues if they attempt to use the old attributes after adding the new tables before I actually remove the columns.
class Asset(Model):
model = models.CharField(max_length=64, blank=True, null=True)
part_number = models.CharField(max_length=32, blank=True, null=True) # this will be a redundant column to be deprecated
company = models.ForeignKey('Company', models.CASCADE, blank=True, null=True) # this will be a redundant column to be deprecated
# other database fields as attributes and class methods
My understanding is that I would need to add something along the lines of warnings.warn('<field name> is deprecated', DeprecationWarning) somewhere in the class, but where would I add it?
Perhaps you could use Django's system check framework (introduced in Django 1.7).
Some interesting examples, using the system-check-framework for deprecation of custom fields, are provided in the migrations docs.
It seems you can also use this approach to mark standard fields on your model.
Applied to the example from the original post, the following works for me (tested in Django 3.1.6).
class Asset(Model):
...
company = models.ForeignKey('Company', models.CASCADE, blank=True, null=True)
company.system_check_deprecated_details = dict(
msg='The Asset.company field has been deprecated.',
hint='Use OtherModel.other_field instead.',
id='fields.W900', # pick a unique ID for your field.
)
...
See the system check API reference for more detailed information, e.g. about the "unique ID".
The following warning will then show, whenever you call runserver, migrate, or other commands, as mentioned in the docs:
System check identified some issues:
WARNINGS:
myapp.Asset.company: (fields.W900) The Asset.company field has been deprecated.
HINT: Use OtherModel.other_field instead.
Also nice to know (from the docs):
... For performance reasons, checks are not run as part of the WSGI stack that is used in deployment. ...
You can use django_deprication.DeprecatedField
pip install django-deprecation
then use like this
class Album(models.Model):
name = DeprecatedField('title')
https://github.com/openbox/django-deprecation
I do something similar to this - turn the field into a property and handle the warning there. Note that this will still break any queries you make that filter on the field - just helps with accessing the attribute from instances.
class NewAsset(Model):
model = models.CharField(max_length=64, blank=True, null=True)
class Asset(Model):
#property
def model(self):
log.warning('Stop using this')
return NewAsset.model

How to do a reverse foreignkey lookup for all records in Django?

I'm trying to do a reversed SQL lookup using Django 1.3. I found a lot of related questions, but unfortunately I can only find the answer for doing this on a single record, not on multiple records at once.
Basically, this is my case: I have a model 'Techniques' which looks like this:
class Technique(models.Model):
title = models.CharField(max_length=32)
description = models.CharField(max_length=256)
skill_level = models.IntegerField()
parent_technique = models.ForeignKey('self', blank=True, null=True)
At the moment I just make a list of all the parentless techniques.
t = get_list_or_404(Technique.objects.filter(parent_technique=None))
Now what I want (or at least think I want) is that all the entries in t get an addition field 'childs' that lists all the techniques that list the current technique as their parent. Can anyone help me with this?
Also, if their is another way to achieve what I want, please feel free to contribute it. I've just begun using Django so I might not know about any alternate ways.
Thanks a lot in advance!
This is built-in already - see following relations backwards.
In your case, just do:
obj.technique_set.all()
for each obj in t.
in model add related_name
parent_technique = models.ForeignKey('self', blank=True, null=True, related_name="childrens_tech")
all children techniques of smt_technique are
smt_technique.childrens_tech.all()

Django many to many recursive relationship

I'm not so great with databases so sorry if I don't describe this very well...
I have an existing Oracle database which describes an algorithim catalogue.
There are two tables algorithims and xref_alg.
Algorithims can have parents and children algorithms. Alg_Xref contains these relationships with two foreign keys - xref_alg and xref_parent.
These are the Django models I have so far from the inspectdb command
class Algorithms(models.Model):
alg_id = models.AutoField(primary_key=True)
alg_name = models.CharField(max_length=100, blank=True)
alg_description = models.CharField(max_length=1000, blank=True)
alg_tags = models.CharField(max_length=100, blank=True)
alg_status = models.CharField(max_length=1, blank=True)
...
class Meta:
db_table = u'algorithms'
class AlgXref(models.Model):
xref_alg = models.ForeignKey(Algorithms, related_name='algxref_alg' ,null=True, blank=True)
xref_parent = models.ForeignKey(Algorithms, related_name='algxref_parent', null=True, blank=True)
class Meta:
db_table = u'alg_xref'
On trying to query AlgXref I encounter this:
DatabaseError: ORA-00904: "ALG_XREF"."ID": invalid identifier
So the error seems to be that it looks for a primary key ID which isn't in the table.. I could create one but seems a bit pointless. Is there anyway to get around this? Or change my models?
EDIT: So after a bit of searching it seems that Django requires a model to have a primary key. Life is too short so have just added a primary key. Will this have any impact on performance?
This is currently a limitation of the ORM provided by Django. Each model has to have one field marked as primary_key=True, if there isn't one, the framework automatically creates an AutoField with name id.
However, this is being worked on as we speak as part of this year's Google Summer of Code and hopefully will be in Django by the end of this year. For now you can try to use the fork of Django available at https://github.com/koniiiik/django which contains an implementation (which is not yet complete but should be sufficient for your purposes).
As for whether there is any benefit or not, that depends. It certainly makes the database more reusable and causes less headaches if you just add an auto incrementing id column to each table. The performance impact shouldn't be too high, the only thing you might notice is that if you have a many-to-many table like this, containing only two ForeignKey columns, adding a third one will increase its size by one half. That should, however, be irrelevant as long as you don't store billions of rows in that table.

Optional additional data on ManyToManyField

I have a ManyToManyField in Django, and I want to save additional information for the relation. What I am doing is
class Speaker(models.Model):
name = models.CharField(max_length=50)
title = models.CharField(max_length=100, blank=True)
description = models.TextField(blank=True)
class Event(models.Model):
title = models.CharField(max_length=120)
speakers = models.ManyToManyField(Speaker, blank=True, null=True, through='Role')
class Role(models.Model):
speaker = models.ForeignKey(Speaker)
event = models.ForeignKey(Event)
role = models.CharField(max_length=50, blank=True)
As per documentation, this prevents Django from doing some automatic stuff. What is particularly annoying is that it makes the Speaker list not available when creating an Event in the admin.
I realize that in general Django does not know what to put in the Role.role field. But that is optional (blank=True). I would expect that
either Django recognizes that Role has only optional fields and lets me use the many to many relation as usual (creating the fields with an empty default), or
Django admin lets me add Speakers to a newly created event, and for each such Speaker it asks for the additional information (the value of Role.role).
The second possibility would be more useful and more general than the first. Still Django admin does none of the two: instead the speakers field is removed from the Event.
Is there a way to make Django admin behave as described above?
The solution lies in this answer. Briefly, one should use InlineModelAdmin, as documented here. This realizes exactly the second behaviour I described.

does custom user class break applications in django?

Let's say that I have subclassed User model (CustomUser) properly (as explained here: http://scottbarnham.com/blog/2008/08/21/extending-the-django-user-model-with-inheritance/)
and installed the comments app.
to access the user of a comment in the template I write:
{{comment.user}} # which provides User, not my CustomUser
and therefore,
{{comment.user.CustomProperty}} #does not work.
How can I work around it?
The ForeignKey from comments.Comment is to django's built-in User object, so querying comment.user will give you the parent object (ie: the base User model). However, django inheritance does provide a way to get the subclass version from the superclass:
{{ comment.user.customeruser }}
Which would then allow you to do:
{{ comment.user.customeruser.customproperty }}
I happen to think this is a weakness in Django's inheritance implementation since it doesn't exactly mirror the behaviour you'd expect from object inheritance in Python generally, but at least there is a workaround. Since I'm hardly rushing out to submit a patch with my version of the right behaviour, I can't complain :-)
I agree with Carl Meyer's comment: it could expensive to automatically fetch the subclass without altering the parent model's db table, and returning the instance of the subclass from the parent class query would be inconsistent with Django's promise that a queryset returns the model on which the queryset was run.
I still find in practice, however, that Django's inheritance leads to some awkward extra steps on occasion. Having used Django since 0.91, and knowing that all the different strategies for resolving object-relational mapping issues have tradeoffs, I'm very happy to have inheritance in Django now, and feel that the current implementation is excellent... so I'd hate for my original answer to be construed as a slight against the project.
As such, I thought I would edit this answer to link to an answer Carl himself provided on a solution in cases where you don't know what type the subclass is: How do I access the child classes of an object in Django without knowing the name of the child class?. He offers advice there for using the ContentType framework. Still some indirection involved, but a good, generalizable, option in the toolkit.
As you can see in the comments of that post, it is still controversially discussed, what is the best way.
I tried it by subclassing too, but I ran in many problems, while using profiles work perfectly for me.
class IRCUser(models.Model):
user = models.ForeignKey(User, unique=True)
name = models.CharField(max_length=100, blank= True, null = True )
friends = models.ManyToManyField("IRCUser", blank= True, null = True)
dataRecieved= models.BooleanField(default=False)
creating an IRCUser works like this:
>>> IRCUser(user = User.objects.get(username='Kermit')).save()
EDIT: Why are user_profiles elegant:
Let's assume, we are writing a webapp, that will behave as a multi-protocol chat. The users can provide their accounts on ICQ, MSN, Jabber, FaceBook, Google Talk .....
We are free to create a custom user class by inheritance, that will hold all the additional informations.
class CustomUser(User):
irc_username = models.CharField(blank=True, null=True)
irc_password = models.PasswordField(blank=True, null=True)
msn_username = models.CharField(blank=True, null=True)
msn_password = models.PasswordField(blank=True, null=True)
fb_username = models.CharField(blank=True, null=True)
fb_password = models.PasswordField(blank=True, null=True)
gt_username = models.CharField(blank=True, null=True)
gt_password = models.PasswordField(blank=True, null=True)
....
....
this leads to
data-rows with a lot of zero-values
tricky what-if-then validation
the impossibility, to have more the one account with the same service
So now let's do it with user_profiles
class IRCProfile(models.Model):
user = models.ForeignKey(User, unique=True, related_name='ircprofile')
username = models.CharField()
password = models.PasswordField()
class MSNProfile(models.Model):
user = models.ForeignKey(User, unique=True, related_name='msnprofile')
username = models.CharField()
password = models.PasswordField()
class FBProfile(models.Model):
user = models.ForeignKey(User, unique=True, related_name='fbprofile')
username = models.CharField()
password = models.PasswordField()
the result:
User_profiles can be created when needed
the db isn't flooded by zero-values
n profiles of same type can be assigned to one user
validation is easy
this may lead to a more cryptic syntax in the templates, but we are free to have some shortcuts in our views/template_tags or to use {% with ... %} to flavour it as we want.
I don’t think there is a way around it, because as you said, comment.user is a User, not a CustomUser.
See http://docs.djangoproject.com/en/dev/topics/db/models/#proxy-models:
QUERYSETS STILL RETURN THE MODEL THAT WAS REQUESTED
There is no way to have Django return, say, a MyUser object whenever you query for User objects. A queryset for User objects will return those types of objects. The whole point of proxy objects is that code relying on the original User will use those and your own code can use the extensions you included (that no other code is relying on anyway). It is not a way to replace the User (or any other) model everywhere with something of your own creation.
Maybe in this case, the old way of using UserProfile would be a better choice?
Sorry if I’m not helping much.