quick question: clear an attribute of a model in django - python

i think this is a pretty easy question for you.
I want to clear an attribute of a django-model.
If i have something like this:
class Book(models.Model):
name = models.TextField()
pages = models.IntegerField()
img = models.ImageField()
In an abstract function i want to clear an attribute, but at that time i don't know what type the field has. So examples would be name="", pages=0 or img=None.. Is there a way to do it in a generic way? I search something like SET_TO_EMPTY(book,"pages")
Do you know a function like that?
many thanks in advance!

I don't think there is a clean way of doing it. However, assuming you've set a default value (which you don't have) you can do it like this:
book.img = book._meta.get_field('img').default
Do note that your current model won't allow a None value. To allow those you have to set null=True and blank=True. For pages you need a default=0.

Related

Get variable OneToOneField in Django

Lets say I have a Form model:
class Form(models.Model):
name = models.TextField()
date = models.DateField()
and various "child" models
class FormA(models.Model):
form = models.OneToOneField(Form, on_delete=models.CASCADE)
property_a = models.TextField()
class FormB(models.Model):
form = models.OneToOneField(Form, on_delete=models.CASCADE)
property_b = models.IntegerField()
class FormC(models.Model):
form = models.OneToOneField(Form, on_delete=models.CASCADE)
property_c = models.BooleanField()
a Form can be one AND ONLY ONE of 3 types of forms (FormA, FormB, FormC). Given a Query Set of Form, is there any way I can recover what types of Form (A, B or C) they are?
I would need to get a better understanding of your actual use case to know whether this is a good option for you or not, but in these situations, I would first suggest using model inheritance instead of a one to one field. The code you have there is basically doing what multi-table inheritance already does.
Take a read through the inheritance docs real quick first and make sure that multi-table inheritance makes sense for you as compared to the other options provided by django. If you do wish to continue with multi-table inheritance, I would suggest taking a look at InheritanceManager from django-module-utils.
At this point (if using InheritanceManager), you would be able to use isinstance.
for form in Form.objects.select_subclasses():
if isinstance(form, FormA):
..... do stuff ......
This might sound like a lot of extra effort but IMO it would reduce the moving parts (and custom code) and make things easier to deal with while still handling the functionality you need.
You can check it by name or isinstance.
a = FormA()
print(a.__class__)
print(a.__class__.__name__)
print(isinstance(a, Forma))
outputs:
<class __main__.FormA at 0xsomeaddress>
'FormA'
True
------------------- EDIT -----------------
Ok based on your comment, you just want to know which instance is assigned to your main Form.
So you can do something like this:
if hasattr(form, 'forma'):
# do something
elif hasattr(form, 'formb'):
# do something else
elif hasattr(form, 'formb'):
# do something else
After investigating a bit I came up with this
for form in forms:
#reduces fields to those of OneToOne types
one_to_ones = [field for field in form._meta.get_fields() if field.one_to_one]
for o in one_to_ones:
if hasattr(form,o.name):
#do something
Might have some drawbacks (maybe bad runtime) but serves its purpose for now.
Ideas to improve this are appreciated

Django and multiple joins

I'm trying to do a quite long join, but it seems like django can't handle the last layer. Am I getting it wrong or is there any way around this?
Models.py
class A(models.Model):
object_id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=50)
class B(models.Model):
class_A_object = models.ForeignKey(A, on_delete=models.PROTECT)
value = models.CharField(max_length=50)
class C(models.Model):
class_B_object = models.ForeignKey(B, on_delete=models.PROTECT)
class D(models.Model):
value= models.IntegerField(primary_key=True)
class_C_object = models.ForeignKey(C, on_delete=models.PROTECT)
I'm then trying to select the value in class D when the related class A object name = ABC.
D.objects.filter(class_C_object__class_B_object__class_A_object__name='ABC')
This fails, to begin with pycharm refuses the autocomplete it and if I run it i get a name not defined error.
However, if i drop one layer it works.
D.objects.filter(class_C_object__class_B_object__value='ABC')
I haven't found any documentation mentioning a maximum number of joins, but it feels like there is a limitation here.
Does anyone know if that's the case, and if so, what the best way would be to work around this?
The database is external to me and can not be modified. The only working decent workaround i have at the moment is to use the cursor directly and write the sql by hand. However, this is not ideal for a lot of reasons.
Any help would be much appreciated.
Kind Regards,
Peter
I have solved this, maybe not the neatest solution, but by getting a queryset of A matching name ABC and then using that queryset for a __in filter on D get the result I want, and it's just doing one query.
It works, and is efficient, but the code is not very clean. If anyone has a suggestion of another way to do this i would be very happy to hear.
Thanks,
Peter

Django 'likes' - ManyToManyField vs new model

I'm implementing likes on profiles for my website and I'm not sure which would be the best practice, a ManyToManyField like so:
class MyUser(AbstractBaseUser):
...
likes = models.ManyToManyField('self', symmetrical = False, null = True)
...
or just creating a class Like, like so:
class Like(models.Model):
liker = models.ForeignKey(MyUser, related_name='liker')
liked = models.ForeignKey(MyUser, related_name='liked')
Is one of them a better choice than the other? If so, why?
thanks
The first option should be preffered. If you need some additional fields to describe the likes, you can still use through="Likes" in your ManyToManyField and define the model Likes.
Manipulating the data entries would be also somewhat more pythonic:
# returns an object collection
likes_for_me = MyUser.objects.filter(pk=1).likes
instead of:
me = MyUser.objects.filter(pk=1)
likes_for_me = Like.objects.filter(liked=me)
The second option is basically what is done internally: a new table is created, which is used to create the links between the entities.
For the first option, you let django do the job for you.
The choice is certainly more about how you want to do the requests. On the second options, you would have to query the Like models that match you model, while on the first one, you only have to request the MyUser, from which you can access the connections.
Second option is more flexible and extensible. For example, you'll probably want to track when like was created (just add Like.date_created field). Also you'll probably want to send notification to content author when content was liked. But at first like only (add Like.cancelled boolead field and wrap it with some logic...).
So I'll go with separate model.
I think the one you choose totally depends on the one you find easier to implement or better. I tend to always use the first approach, as it is more straightforward and logical, at least to me. I also disagree with Igor on that it's not flexible and extensible, you can also initiate notifications when it happens. If you are going to use the Django rest framework, then I totally suggest using the first method, as the second could be a pain.
class Post(models.Model):
like = models.ManyToManyField(settings.AUTH_USER_MODEL, blank=True, related_name='post_like')
Then in your view, you just do this.
#api_view(['GET'])
#permission_classes([IsAuthenticated])
def like(request, id):
signed_in = request.user
post = Post.objects.get(id=id)
if signed_in and post:
post.like.add(signed_in)
# For unlike, remove instead of add
return Response("Successful")
else:
return Response("Unsuccessful", status.HTTP_404_NOT_FOUND)
Then you can use the response however you like on the front end.

Best practice for accessing distantly related Django models

Let say we have a long chain of Django models, where each references the one above through a ForeignKey field:
class One(models.Model):
# fields
class Two(models.Model):
one = models.ForeignKey(One)
...
class Ten(models.Model):
nine = models.ForeignKey(Nine)
Good! Now image, if you will, having an instance of the Ten model and wanting to grab the related One instance. This can result in long lines of attribute chaining like this:
ten_instance.nine.eight.seven.six.five.four.three.two.one
I'm wondering what the standard approach would be to this niggling issue. Do we leave it as is, being inherently descriptive and readable. Or do we aim to shorten such a line to make things more simple:
ten_instance.one
 But What's The Best Practice Here? Or is there a more simple solution?
Use Properties
My current approach would be to add a property to the Ten model, abstracting away that attribute chaining:
class Ten(models.Model):
nine = models.ForeignKey(Nine)
#property
def one(self):
return self.nine.eight.seven.six.five.four.three.two.one
I can see a downside to this tactic however, and that's the added mysticism involved. Does the Ten instance actually have a relation to the One model or not? I wouldn't be able to tell without inspecting the model myself.
You probably want to use django-mptt for sophisticated hierarchal models although it can be a bit ott. If you want a simple hierarchy then add a ForeignKey to self:
class Number(models.Model):
parent = models.ForeignKey('self', blank=True, null=True,
related_name='child')
then the query would be something like this based on a unique field, say slug:
Number.objects.get(parent__slug='one')

Database query across django ManyToManyField

I'd like to find how to select all objects whose ManyToMany field contains another object. I have the following models (stripped down)
class Category(models.Model):
pass
class Picture(models.Model):
categories = models.ManyToManyField(Category)
visible = models.BooleanField()
I need a function to select all the Pictures in one or more Categories:
def pics_in_cats(cat_ids=()):
pass
BUT it needs to return a QuerySet if possible so that I can do something like:
pics_in_cats((1,2,3)).filter(visible=True)
It could be done by loading all the relevant Category objects and merging their picture_set attributes, but that seems inefficient. I'd also like to avoid falling back to raw SQL if possible.
Thanks in advance
Why write a custom function and not use something like this? (untested)
pics = Picture.objects.filter(categories__in = [1,2,3]).filter(visible=True)

Categories