problem restricting deletion of a django model value - python

I have a foreign key, that when the object it references gets deleted will be filled with a default value via a callable on_delete=models.SET(callable).. However, I have problem with that because in the Django admin site I'm able to delete the callable value that it has passed to the foreign key and since my foreign key can't be null this is going to raise a server error which I don't like.. so I want to handle that error through the Django admin site itself if that is possible and I know it is.. also if you have better thoughts on this, I really appreciate your help.
As a temporary solution I had overrode the QuerySet > delete() method of the referenced model to skip deleting the object through the Django admin site bulk deletion. I'm sure that there is a better way than this.
The foreign key:
class Product(models.Model):
marektplace_tag = models.ForeignKey('tags.MarketplaceTag', on_delete=models.SET(default_marketplace_tag))
the callable:
# Model: Product
def default_marketplace_tag():
from tags.models import MarketplaceTag
tag = MarketplaceTag.objects.get_or_create(name='None')
return tag[0].id
The referenced model:
class MarketplaceTag(models.Model):
name = models.CharField(max_length=32, validators=[only_letters])

Related

How to make a username as a foreign key of another model in django

I am very new to Django. I want to link a model which has 2 field 'username' and 'password'. I want to make 'username' field as as Foreign in another model. But as per Django we can only pass the whole Model Object, who is referring to as it's foreign key.
am I wrong somewhere? please give me any solution regarding this basic problem.
No you can link to any unique field of a Django model. So if your models look like:
class Target(models.Model):
name = models.CharField(max_length=128, unique=True)
class SourceModel(models.Model):
target = models.ForeignKey(Target, to_field='name', on_delete=models.CASCADE)
You can assign the value of the target column to the target_id then. So for example:
Target.objects.create(name='target1')
SourceModel.objects.create(target_id='target1')
So you do not need to pass a Target object itself. You can use the …_id "twin" field to use the target column value. The database will normally enforce referential integrity, and thus will prevent passing a non-existing value to the foreign key column.

Cannot delete some instances of model because of FK

Python 3.6 and Django 1.11.7.
I've got two Models look like the following:
class User():
name = models.CharField()
...
class UserInfo():
user = models.OneToOneField(User, on_delete=models.PROTECT, primary_key=True, related_name='info')
I wanted to delete some user instance A, and I explicitly deleted user A's info. But when I tried to delete the user model user.delete(), I got the ProtecedError:
ProtectedError: ("Cannot delete some instances of model 'User' because they are referenced through a protected foreign key: 'UserInfo.user'", <QuerySet [<UserInfo: UserInfo object>]>)
Then I tried to put the delete inside a try/catch looks like follows:
try:
user.delete()
except ProtectedError:
UserInfo.objects.filter(user=user).delete()
user.delete()
But still got the same exception. What might went wrong in my operation?
You are using a protect clause on the related objects:
on_delete=models.PROTECT
You can check it in the documentation on:
https://docs.djangoproject.com/en/2.2/ref/models/fields/#django.db.models.ForeignKey.on_delete
You have it pointed here:
PROTECT[source]
Prevent deletion of the referenced object by raising ProtectedError, a subclass of django.db.IntegrityError.
Remove the on_delete=models.PROTECT on your user field. And run manage.py makemigrations
ForeignKey fields have a default value of CASCADE for the on_delete argument. This means that deleting the user object will cascade down and also delete the userinfo object linked to that user.
Looks like this is the behaviour you are looking for.
You can read more about this in the documentation documentation
Also note although on_delete has a default value fo CASCADE this argument will be required from Django 2.0.

How do I get the manager object for a Model foreign key field?

I'm trying to retrieve the Manager (or Model) for a Django foreign key. This should be straightforward but I can't seem to find the right attribtues.
class ModelA(models.Model):
pass
class ModelB(models.Model):
thing = models.ForeignKey(ModelA)
Let's say I have the variable modelBInstance and the string "thing" and I want to get ModelA. I've followed (exhaustively, I think) the obvious looking attributes of each object using getattr. Either I'm missing something or it's not possible. And I don't think it's not possible.
All the relevant information about fields is stored in the _meta class of the model. In there, you'll see a get_field_by_name method that will return the actual foreign key field. From there, you can get the model it points at via rel.to.
thing = ModelB._meta.get_field_by_name('thing')[0]
print thing.rel.to
Couple of things:
1. Model instances don't have managers, Model's do.
2. To get the manager, of the foreign key you will have to first reference its class and then reference it manager.
type(getattr(modelBInstance,'thing')).objects would give you access to the manager.

Traverse foreign key and pull remote model data into Django admin

Is is possible in the admin to pull a field from a remote model, if you have a local foreign key pointing to that model?
class FirstModel(models.Model):
[...]
value12 = models.CharField()
class SecondModel(models.Model):
[...]
firstmodel = models.ForeignKey(FirstModel)
And in the Admin I want to pull in value12, any time someone views/edits SecondModel. I figure I can do this through Inlines, but then I lose Fields and FieldSets ordering. Any other options? Ideal results would be sortable with fields/fieldsets, -and- read-only.
You should be able to access any field in the first model as: firstmodel__value12
For the list view for the SecondModel:
list_display = ('firstmodel__value12',)
For the edit view you can use formfield_overrides. To make it non-editable you specify a read-only widget, e.g. like this one or provide your own.

Django ForeignKey which does not require referential integrity?

I'd like to set up a ForeignKey field in a django model which points to another table some of the time. But I want it to be okay to insert an id into this field which refers to an entry in the other table which might not be there. So if the row exists in the other table, I'd like to get all the benefits of the ForeignKey relationship. But if not, I'd like this treated as just a number.
Is this possible? Is this what Generic relations are for?
This question was asked a long time ago, but for newcomers there is now a built in way to handle this by setting db_constraint=False on your ForeignKey:
https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.db_constraint
customer = models.ForeignKey('Customer', db_constraint=False)
or if you want to to be nullable as well as not enforcing referential integrity:
customer = models.ForeignKey('Customer', null=True, blank=True, db_constraint=False)
We use this in cases where we cannot guarantee that the relations will get created in the right order.
EDIT: update link
I'm new to Django, so I don't now if it provides what you want out-of-the-box. I thought of something like this:
from django.db import models
class YourModel(models.Model):
my_fk = models.PositiveIntegerField()
def set_fk_obj(self, obj):
my_fk = obj.id
def get_fk_obj(self):
if my_fk == None:
return None
try:
obj = YourFkModel.objects.get(pk = self.my_fk)
return obj
except YourFkModel.DoesNotExist:
return None
I don't know if you use the contrib admin app. Using PositiveIntegerField instead of ForeignKey the field would be rendered with a text field on the admin site.
This is probably as simple as declaring a ForeignKey and creating the column without actually declaring it as a FOREIGN KEY. That way, you'll get o.obj_id, o.obj will work if the object exists, and--I think--raise an exception if you try to load an object that doesn't actually exist (probably DoesNotExist).
However, I don't think there's any way to make syncdb do this for you. I found syncdb to be limiting to the point of being useless, so I bypass it entirely and create the schema with my own code. You can use syncdb to create the database, then alter the table directly, eg. ALTER TABLE tablename DROP CONSTRAINT fk_constraint_name.
You also inherently lose ON DELETE CASCADE and all referential integrity checking, of course.
To do the solution by #Glenn Maynard via South, generate an empty South migration:
python manage.py schemamigration myapp name_of_migration --empty
Edit the migration file then run it:
def forwards(self, orm):
db.delete_foreign_key('table_name', 'field_name')
def backwards(self, orm):
sql = db.foreign_key_sql('table_name', 'field_name', 'foreign_table_name', 'foreign_field_name')
db.execute(sql)
Source article
(Note: It might help if you explain why you want this. There might be a better way to approach the underlying problem.)
Is this possible?
Not with ForeignKey alone, because you're overloading the column values with two different meanings, without a reliable way of distinguishing them. (For example, what would happen if a new entry in the target table is created with a primary key matching old entries in the referencing table? What would happen to these old referencing entries when the new target entry is deleted?)
The usual ad hoc solution to this problem is to define a "type" or "tag" column alongside the foreign key, to distinguish the different meanings (but see below).
Is this what Generic relations are for?
Yes, partly.
GenericForeignKey is just a Django convenience helper for the pattern above; it pairs a foreign key with a type tag that identifies which table/model it refers to (using the model's associated ContentType; see contenttypes)
Example:
class Foo(models.Model):
other_type = models.ForeignKey('contenttypes.ContentType', null=True)
other_id = models.PositiveIntegerField()
# Optional accessor, not a stored column
other = generic.GenericForeignKey('other_type', 'other_id')
This will allow you use other like a ForeignKey, to refer to instances of your other model. (In the background, GenericForeignKey gets and sets other_type and other_id for you.)
To represent a number that isn't a reference, you would set other_type to None, and just use other_id directly. In this case, trying to access other will always return None, instead of raising DoesNotExist (or returning an unintended object, due to id collision).
tablename= columnname.ForeignKey('table', null=True, blank=True, db_constraint=False)
use this in your program

Categories