How to Fake django.contrib.auth User value for specific user? - python

I am writing a script and want to fake a User with an id (PK) of 2
var = Table(FKToUser=2)
var.save()
the problem is I'm getting :
"Table.FKToUser" must be a "User" instance
I've verified that the auth_user has a record with id=2
How can I fake this 2 value for testing purposes?
Thank you!

Assuming that Table is your model and FKToUser is a foreign key, there are two ways. The first is to set the FKToUser_id attribute to 2 and save the model. The other is to fetch the user and set the FKToUser attribute to the right user model instance.
This is also basically how a foreign key works. The actual column in the database is the FKToUser_id column, and it's a simple Integer foreign key to an id in another column. Django magic makes it possible to automatically retrieve the right instance by accessing FKToUser, and to set the right value by assigning a model instance to FKToUser.

Related

Is there a way to make the 'One' table optional in OneToManyRelationship in Django

I have two tables, one is Anonym the other is Userdatabase. I want my app to work without requiring any login info therefore it will work with Anonym only by using the deviceid of the user to process account information. If however, a user wants to access extra features they need to create a user with username/password. Then I will process the data using Userdatabase table. A user can have multiple devices so there is a OneToMany relationship in there, but a device doesn't have to have a User (they don't need to register) which breaks the relationship. Is there a way to make the Userdatabase table optional while keeping the OneToMany relationship? Perhaps by inserting a method or another class within UserDatabase? Please find the code below:
--Models--
class Anonym(models.Model):
deviceid=models.ForeignKey(Userdatabase,max_length=200,on_delete=models.SET_NULL,null=True)
accounttype=models.TextField(default='Free')
numberofattempts=models.IntegerField(default=0)
created=models.DateField(auto_now_add=True)
class Userdatabase(models.Model):
username=models.CharField(max_length=20,unique=True)
password=models.CharField(max_length=20)
deviceid=models.TextField(default='inputdeviceid')
accounttype=models.TextField(default='Free')
numberofattempts=models.IntegerField(default=0)
created=models.DateField(auto_now_add=True)
--urls--
urlpatterns=[path('deviceregister/<str:id>/',views.deviceregistration)]
--views--
def deviceregistration(request,id):
import time
deviceid=id
newdevice-models.Anonym(created=time.strftime("%Y-%m-%d"),deviceid=deviceid)
newdevice.save()
return HttpResponse('Succesful registration')
When I send a request as '/deviceregister/123456/' for example, django raises an ValueError saying Cannot assign "'123456'": "Anonym.deviceid" must be a "Userdatabase" instance.
you should search by fieldname, which contains id. in your case it is deviceid_id.
newdevice=models.Anonym(created=time.strftime("%Y-%m-%d"),deviceid_id=deviceid)
deviceid in your case should be Userdatabase.objects.get(pk=id)
deviceid=Userdatabase.objects.get(pk=id)
newdevice=models.Anonym(created=time.strftime("%Y-%m-%d"),deviceid=deviceid)
in my opinion - field names in your project really can confuse anyone
If you do not want to change your model, you can just link any newly-added device to a dummy user. When later a user want to link a device, replace dummy with the real user.
If you can change your model, you can remove the foreign key relationship, and add another table which links the id of both side: one field for deviceid and the other userid.
I know both options kind of smell, but at least they should work :)

creating a many to one relationship in django with an existing model

How can I create a many to one relationship with an existing model in django? Looking at the documentation, You can see that you can create a foreign key and create and object with a reference to the other model.
For example,
r = Reporter(first_name='John', last_name='Smith', email='john#example.com')
r.save()
a = Article(id=None, headline="This is a test", pub_date=date(2005, 7, 27), reporter=r)
a.save()
You can then access the id of r with a.reporter.id.
One problem with this, however is that if you wanted to check the id of r through a, you have to create a in order to do it.
How would you do this with an already existing model?
As an example, if I have a user and I want the user to be able to create multiple characters for a game, how can I assign the character a foreign key to the user if the user already exists?
Looking at this answer, you see that you need to give the model that you want to reference to the foreign key, but it doesn't actually explain how to do it.
How would you do this with an already existing model?
It's unclear which model you're referring to. If you mean an existing reporter, you'd get it and do it exactly the same way:
r = Reporter.objects.get(email='john#example.com')
a = Article(headline="This is a test", pub_date=date(2005, 7, 27), reporter=r)
a.save()
If you mean an existing article, you can change the foreign key just like any model instance field:
a = Article.objects.get(headline="This is a test")
a.r = Reporter.objects.create(...) # or .get() depending on what you want.
a.save()
How can I assign the character a foreign key to the user if the user already exists?
Using the same logic, you'd get the user and create a new character with this existing user object:
# Get user, or if this was the logged in user maybe just request.user
user = User.objects.get(username='wanderer')
# Create the character, using the existing user as a foreign key
# (the .create is just shorthand for creating the object then saving it)
Character.objects.create(character_name='Trogdor', user=user)
# Or alternatively, you can simply use the implicit reverse relationship
user.character_set.create(character_name='Homestar Runner')

Django 1.9 updating model object makes a new object instance

I am working with Django 1.9 and noticed some strange behavior when working with the models. I know that the following code creates an object, saves it to the database, changes the field, then updates that same entry in the database:
cat = models.Cat(name="Bob")
cat.save()
cat.name = "Sally"
cat.save()
However, when I query all my objects using cats = models.Cat.objects.all() I find that rather than returning ["Sally"] it actually returns ["Bob", "Sally"]. Apparently cat.save() is creating a new element in the database rather than updating an existing one. I've worked with Django before, but never had this issue. One thing to note is that the name attribute is the primary key for the Cat model. Could this be why it's not updating, but creating a whole new entry?
The primary key is what Django uses to determine whether to update or create an item. Usually, that's an opaque ID which you don't modify; but in your case, it's part of your data. When you modify the value, Django has no way of knowing that the object refers to an existing row in the database.
Don't do this; stick with autoincremented IDs that have no relation to your actual data.
You're right, the issue here is that your primary key is the name field. Django will do an update if the pk value exists in the database, and an insert if it doesn't. For example:
class Cat(models.Model):
name = models.CharField(max_length=255)
cat = Cat(name='Bart')
cat.save() # This creates a new object
print(cat.pk)
> '1'
cat.name = 'Sally'
cat.save() # This updates the object, the pk will still be '1'
print(cat.pk)
> '1'
print(Cat.objects.all())
> [<Cat 'Sally'>]
fluffy = Cat(name='Fluffy')
fluffy.pk = 1
fluffy.save()
'''This will UPDATE the existing object, since an object
with that primary key already exists'''
print(Cat.objects.all())
> [<Cat 'Fluffy'>]
fluffy.pk = 2
fluffy.save()
'''This will CREATE a new object, since no object with
that primary key already exists'''
print(Cat.objects.all())
> [<Cat 'Fluffy'>, <Cat 'Fluffy'>]
If possible, I would recommend removing the primary_key=True attribute on the name field. If you want name to be unique, maybe just set unique=True instead?

Get model instance id from foreign key field without loading object

Say we have the following class:
Class Alert(models.Model):
contact = models.ForeignKey('emails.Contact',null=True,blank=True)
If I wanted to get the foreign key of the contact I would do somealert.contact.pk or somealert.contact_id. Do these commands pull down the whole contact object and then get the key? Or do any of them just yield the foreign key without pulling all off the attributes of the instance from the database. I worried about performance and would prefer to just get the key itself.
The first one - somealert.contact.pk - will get the Contact object. The second - somealert.contact_id - won't.
You can verify this in the shell by looking at the contents of django.db.connection.queries.

Django unique together constraint failure?

Using Django 1.5.1. Python 2.7.3.
I wanted to do a unique together constraint with a foreign key field and a slug field. So in my model meta, I did
foreign_key = models.ForeignKey("self", null=True, default=None)
slug = models.SlugField(max_length=40, unique=False)
class Meta:
unique_together = ("foreign_key", "slug")
I even checked the table description in Postgres (9.1) and the constraint was put into the database table.
-- something like
"table_name_foreign_key_id_slug_key" UNIQUE CONSTRAINT, btree (foreign_key_id, slug)
However, I could still save into the database table a foreign_key of None/null and duplicate strings.
For example,
I could input and save
# model objects with slug="python" three times; all three foreign_key(s)
# are None/null because that is their default value
MO(slug="python").save()
MO(slug="python").save()
MO(slug="python").save()
So after using unique_together, why can I still input three of the same valued rows?
I'm just guessing right now that it might have to do with the default value of None for the foreign_key field, because before the unique_together, when I just had unique=True on slug, everything worked fine. So if that is the case, what default value should I have that indicates a null value, but also maintains the unique constraint?
In Postgresql NULL isn't equal to any other NULL. Therefore the rows you create are not the same (from Postgres' perspective).
Update
You have a few ways to deal with it:
Forbid the Null value for foreign key and use some default value
Override the save method of your model to check that no such row exists
Change SQL standard :)
Add a clean method to your model, so you can edit an existing row.
def clean(self):
queryset = MO.objects.exclude(id=self.id).filter(slug=self.slug)
if self.foreign_key is None:
if queryset.exists():
raise ValidationError("A row already exists with this slug and no key")
else:
if queryset.filter(foreign_key=self.foreign_key).exists():
raise ValidationError("This row already exists")
Beware, clean (or full_clean) isn't called by the default save method.
NB: if you put this code in the save method, update forms (like in the admin) won't work: you will have a traceback error due to the ValidationError exception.
Just manually create secondary index on slug field, but only for NULL values in foreign_key_id:
CREATE INDEX table_name_unique_null_foreign_key
ON table_name (slug) WHERE foreign_key_id is NULL
Please note, that Django does not support this, so without custom form/model validation you will get pure IntegrityError / 500.
Possible duplicate of Create unique constraint with null columns
As hobbyte mentioned, "In Postgresql NULL isn't equal to any other NULL. Therefore the rows you create are not the same (from Postgres' perspective)."
Another possible way to address this challenge is to add custom validation at the view level in the form_valid method.
In views.py:
def form_valid(self, form):
--OTHER VALIDATION AND FIELD VALUE ASSIGNMENT LOGIC--
if ModelForm.objects.filter(slug=slug,foreign_key=foreign_key:
form.add_error('field',
forms.ValidationError( _("Validation error message that shows up in your form. "),
code='duplicate_row', ))
return self.form_invalid(form)
This approach is helpful if you are using class based views, especially if you are automatically assigning values to fields that you want to hide from the user.
Pros:
You don't have to create dummy default values in the database
You can still use update forms (see Toff's answer)
Cons:
- This doesn't protect against duplicate rows created directly at the database level.
- If you use Django's admin backend to create new MyModel objects, you'll need to add this same validation logic to your admin form.

Categories

Resources