I am getting empty in return (from my model methods), I don't get where I am wrong in this, we can query model using self
class SocialLinks(models.Model):
alias_name = models.CharField(max_length=10,)
name = models.CharField(max_length=30)
url_link = models.URLField()
def get_fb_link(self):
try:
fb = self.objects.get(alias_name='fb')
return fb.url_link
except:
return ""
def get_linkdin_link(self):
try:
linkdin = self.objects.get(alias_name='linkedin')
return linkdin
except:
return ""
def get_insta_link(self):
try:
insta = self.objects.get(alias_name='insta')
return insta.url_link
except:
Your issue is that self corresponds to one instance of the model class, not the class itself.
So you can do
all_fb_links = SocialLinks.objects.filter(alias_name="fb")
and you will get all the records from the model that are facebook links, but you cannot do this referencing a single instance of the record using self.
You could write a class method, but what you actually want here is a model manager to define some specific queries so that you can then do
SocialLinks.get_all_fb_links()
Here's the docs on defining a custom manager: https://docs.djangoproject.com/en/3.2/topics/db/managers/
A method on the class like you are defining would be used to return something not stored on the table, but which could perhaps be derived from it. A simple example might be:
def link_type_and_url(self):
return f"{self.alias_name}:{url_link}"
Related
I got a Customer model which is related to a Company model. I'd like to give my factory the possibility to use a given company (if I need several customer from the same company). I thought to use the inner class Params to achieve that, but I got an issue using LazyAttribute and SubFactory together. Here is my factory:
class CustomerFactory(UserFactory):
"""
To be valid, customer needs to belong to one of these groups:
- manager
- regular
This can be achieved using the `set_group` parameter.
Example:
manager = CustomerFactory(set_group=manager_group)
"""
#lazy_attribute
def _company(self):
if self.use_company:
return self.use_company
else:
return SubFactory('rdt.tests.factories.CompanyFactory')
class Meta:
model = Customer
class Params:
use_company = None
#post_generation
def set_group(self, create, extracted, **kwargs):
if extracted:
self.groups.add(extracted)
I thought to use the factory as:
c1 = factories.CustomerFactory(use_company=my_company)
c2 = factories.CustomerFactory()
I got ValueError. It seems I can't get the parameter value 'use_company' in the factory.
Anyway my factory throws a ValueError.
I found a solution simply using an other post_generation method like this:
#post_generation
def set_group(self, create, extracted, **kwargs):
if extracted:
self.groups.add(extracted)
#post_generation
def company(self, create, extracted, **kwargs):
if extracted:
self._company = extracted
self.save()
What I'm doing is posting a list of primary keys with other data to an endpoint and I'm creating an instance of an object per id that is in the list. The problem is that I want to then return all the objects I created but only one instance of the object is expected so it doesn't work.
The data I'm posting to /convo/:id/members/ looks like this:
{
member_ids = [5,1,2,6]
}
Then in the view I'm passing the current user to my serializer (needed for the object I'm creating because it has an added_by field.
I'm doing this by overriding perform_create. It looks ca like this:
def perform_create(self, serializer):
convo = Conversation.objects.get(pk = self.kwargs['pk])
serializer.save(added_by = self.request.user, convo = convo)
Then I'm overriding the create method in the serializer, take each id in the data received and create a member in the conversation. This is how that looks like ca (pseudocode)
def create(self, validated_data):
added_by = validated_data.pop('added_by')
users = validated_data.pop('member_ids')
convo = validated_data.pop('convo')
added_members = []
for u in users:
added_members.append(ConvoMember.objects.create(convo = convo, user = u, added_by = added_by))
return added_members // this fails because its expecting only one instance of ConvoMember
// but i still need to be able to do this somehow.
How do I go about returning all of the objects i created instead of having to return only the last one or something?
I've got following model:
class Hospitalization(models.Model):
patient = models.ForeignKey(Patient)
room = models.ForeignKey(Room)
date_in = models.DateField()
date_out = models.DateField(blank=True, null=True)
...
I'd like to list the current Hospitalizations. So I added a #property 'is_current':
#property
def is_current(self):
today = date.today()
if self.date_in and self.date_out:
if self.date_in <= today and self.date_out >= today:
return True
if self.date_in and not self.date_out:
if self.date_in <= today:
return True
When trying to call the property from a filter in my views.py however, I get following error though: *Cannot resolve keyword 'is_current' into field. Choices are: date_in, date_out, id, patient, room*
Then I thought I could do this with a Manager. So I added a Manager:
class Hospitalization(models.Model):
def get_query_set(self):
today = date.today()
if self.date_in and self.date_out:
return qs.filter(date_in__lte=today, date_out__gte=today)
if self.date_in and not self.date_out:
return qs.filter(date_in__lte=today)
But that doesn't work either: *AttributeError: 'HospitalizationManager' object has no attribute 'date_in'*
What would be the Django recommended way to fix this?
There are various things wrong with your Manager:
You're subclassing Model, not Manager.
You're using your model attributes as if they belonged to the Manager, which they don't.
Your custom get_queryset isn't calling the superclass method, so it's using an undefined qs attribute.
The correct way to define your manager would be:
class CurrentHospitalizationManager(models.Manager):
def get_query_set(self):
qs = super(CurrentHospitalizationManager, self).get_query_set()
today = date.today()
return qs.filter(
# we can use Q objects here to check if the date_out exists or not
# and compare against the current date if necesary
models.Q(date_out__isnull=True) | models.Q(date_out__gte=today),
date_in__lte=today
)
Then you should assign the manager to a class attribute on your model, like this
class Hospitalization(models.Model):
current_objects = CurrentHospitalizationManager()
...
And use it on your code like this:
Hospitalization.current_objects.get(...) # or filter, or whatever
I don't recommend you assign this custom manager to your default manager attr (objects), since you wouldn't be able to access the Hospitalization's instances that aren't "current".
Custom Manager's documentation
I am trying to create a custom cleaning method which look in the db if the value of one specific data exists already and if yes raises an error.
I'm using a model form of a class (subsystem) who is inheriting from an other class (project).
I want to check if the sybsystem already exists or not when i try to add a new one in a form.
I get project name in my view function.
class SubsytemForm(forms.ModelForm):
class Meta:
model = Subsystem
exclude = ('project_name')
def clean(self,project_name):
cleaned_data = super(SubsytemForm, self).clean(self,project_name)
form_subsystem_name = cleaned_data.get("subsystem_name")
Subsystem.objects.filter(project__project_name=project_name)
subsystem_objects=Subsystem.objects.filter(project__project_name=project_name)
nb_subsystem = subsystem_objects.count()
for i in range (nb_subsystem):
if (subsystem_objects[i].subsystem_name==form_subsystem_name):
msg = u"Subsystem already existing"
self._errors["subsystem_name"] = self.error_class([msg])
# These fields are no longer valid. Remove them from the
# cleaned data.
del cleaned_data["subsystem_name"]
return cleaned_data
My view function :
def addform(request,project_name):
if form.is_valid():
form=form.save(commit=False)
form.project_id=Project.objects.get(project_name=project_name).id
form.clean(form,project_name)
form.save()
This is not working and i don't know how to do.
I have the error : clean() takes exactly 2 arguments (1 given)
My model :
class Project(models.Model):
project_name = models.CharField("Project name", max_length=20)
Class Subsystem(models.Model):
subsystem_name = models.Charfield("Subsystem name", max_length=20)
projects = models.ForeignKey(Project)
There are quite a few things wrong with this code.
Firstly, you're not supposed to call clean explicitly. Django does it for you automatically when you call form.is_valid(). And because it's done automatically, you can't pass extra arguments. You need to pass the argument in when you instantiate the form, and keep it as an instance variable which your clean code can reference.
Secondly, the code is actually only validating a single field. So it should be done in a specific clean_fieldname method - ie clean_subsystem_name. That avoids the need for mucking about with _errors and deleting the unwanted data at the end.
Thirdly, if you ever find yourself getting a count of something, iterating through a range, then using that index to point back into the original list, you're doing it wrong. In Python, you should always iterate through the actual thing - in this case, the queryset - that you're interested in. However, in this case that is irrelevant anyway as you should query for the actual name directly in the database and check if it exists, rather than iterating through checking for matches.
So, putting it all together:
class SubsytemForm(forms.ModelForm):
class Meta:
model = Subsystem
exclude = ('project_name')
def __init__(self, *args, **kwargs):
self.project_name = kwargs.pop('project_name', None)
super(SubsystemForm, self).__init__(*args, **kwargs)
def clean_subsystem_name(self):
form_subsystem_name = self.cleaned_data.get("subsystem_name")
existing = Subsystem.objects.filter(
project__project_name=self.project_name,
subsytem_name=form_subsystem_name
).exists()
if existing:
raise forms.ValidationError(u"Subsystem already existing")
return form_subsystem_name
When you do form=form.save(commit=False) you store a Subsystem instance in the variable form but the clean method is defined in SubsystemForm. Isn't it?
I made a custom manager that has to randomize my query:
class RandomManager(models.Manager):
def randomize(self):
count = self.aggregate(count=Count('id'))['count']
random_index = random.randint(0, count - 1)
return self.all()[random_index]
When I use the method defined in my manager in the first place, it's works ok:
>>> PostPages.random_objects.randomize()
>>> <PostPages: post 3>
I need to randomize the already filtered query. When I tried to use the manager and the method in chain I got an error:
PostPages.random_objects.filter(image_gallary__isnull=False).randomize()
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
/home/i159/workspace/shivaroot/shivablog/<ipython-input-9-98f654c77896> in <module>()
----> 1 PostPages.random_objects.filter(image_gallary__isnull=False).randomize()
AttributeError: 'QuerySet' object has no attribute 'randomize'
Result of filtering is not an instance of model class, but it's django.db.models.query.QuerySet, so that it does not have my manager and method, respectively.
Is there a way to use custom manager in chain query?
This is how you chain custom methods on custom manager ie: Post.objects.by_author(user=request.user).published()
from django.db.models.query import QuerySet
class PostMixin(object):
def by_author(self, user):
return self.filter(user=user)
def published(self):
return self.filter(published__lte=datetime.now())
class PostQuerySet(QuerySet, PostMixin):
pass
class PostManager(models.Manager, PostMixin):
def get_query_set(self):
return PostQuerySet(self.model, using=self._db)
Just a code example using the new as_manager() method (see update information from #zzart.
class MyQuerySet(models.query.QuerySet):
def randomize(self):
count = self.aggregate(count=Count('id'))['count']
random_index = random.randint(0, count - 1)
return self.all()[random_index]
class MyModel(models.Model):
.....
.....
objects = MyQuerySet.as_manager()
.....
.....
And then you will be able to use something like this in your code:
MyModel.objects.filter(age__gt=16).randomize()
As you can see, the new as_manager() is really neat:)
Looks like this snippet provides a solution to your situation: Custom managers with chainable filters.
Given that you have an existing models.Manager and you don't want to expose some of the manager method to a chainable queryset, you can use Manager.from_queryset(QuerySet)().
So, you could still place all your chainable queryset method inside the QuerySet and your manager method independently.
Example given in the official site.
Snippet from Django Docs
class BaseManager(models.Manager):
# Available only on Manager.
def manager_only_method(self):
return
class CustomQuerySet(models.QuerySet):
# Available on both Manager and QuerySet.
def manager_and_queryset_method(self):
return
# Available only on QuerySet.
def _private_method(self):
return
CustomManager = BaseManager.from_queryset(CustomQuerySet)
class MyModel(models.Model):
objects = CustomManager()
How about something like below which creates the custom QuerySet dynamically and allows us to 'transplant' our custom queries onto the returned QuerySet instance:
class OfferManager(models.Manager):
"""
Additional methods / constants to Offer's objects manager
"""
### Model (db table) wide constants - we put these and
### not in model definition to avoid circular imports.
### One can access these constants through like
<foo>.objects.STATUS_DISABLED or ImageManager.STATUS_DISABLED
STATUS_DISABLED = 0
...
STATUS_CHOICES = (
(STATUS_DISABLED, "Disabled"),
(STATUS_ENABLED, "Enabled"),
(STATUS_NEGOTIATED, "Negotiated"),
(STATUS_ARCHIVED, "Archived"),
)
...
# we keep status and filters naming a little different as
# it is not one-to-one mapping in all situations
QUERYSET_PUBLIC_KWARGS = {'status__gte': STATUS_ENABLED}
QUERYSET_ACTIVE_KWARGS = {'status': STATUS_ENABLED}
def get_query_set(self):
""" our customized method which transpalats manager methods
as per get_query_set.<method_name> = <method> definitions """
CustomizedQuerySet = QuerySet
for name, function in self.get_query_set.__dict__.items():
setattr(CustomizedQuerySet, name, function)
return CustomizedQuerySet(self.model, using=self._db)
def public(self):
""" Returns all entries accessible through front end site"""
return self.all().filter(**OfferManager.QUERYSET_PUBLIC_KWARGS)
get_query_set.public = public # will tranplat the function onto the
# returned QuerySet instance which
# means 'self' changes depending on context.
def active(self):
""" returns offers that are open to negotiation """
return self.public().filter(**OfferManager.QUERYSET_ACTIVE_KWARGS)
get_query_set.active = active
...
More polished version of this method and django ticket here: https://code.djangoproject.com/ticket/20625.