简体   繁体   中英

How to get a property from a Django model?

class Jornal(models.Model):
    url = models.URLField(unique = True,blank=True)
    name = models.CharField(max_length=100) 

    def __str__(self):
        return self.name
    @property
    def domain(self):
        return urlparse(self.url).netloc

How to I get the Jornal object through its domain? I was trying this in the shell:

domain = "www.example.com"
obj = Jornal.objects.get_domain(domain)

and then this:

domain = "www.example.com"
obj = Jornal.objects(domain = domain)

But none works.

EDIT I was trying with a get method because I want DoesNotExist in case it is not found.

You cannot query for properties of a model. You can check if the url contains the domain in it, but this would return all entries in your database which have the same domain.

from django.core.exceptions import ObjectDoesNotExist


domain = 'www.example.com'
journal_qs = Journal.objects.filter(url__icontains=domain) # Case insensitive
# Journal.objects.filter(url__contains=domain) # Case sensitive

if journal_qs.exists():
    # Some logic here because there exists some value
else:
   raise ObjectDoesNotExist

Since the property is stored at runtime, we can - if we consider the property to be a blackbox - only do this by filtering at the level of Python. So for instance with list comprehension:

domain = "www.example.com"
all_example_domain = [j for j in Journal.objects.all() if j.domain == domain]

We can not - in general - reverse a function, so we can not use filtering at the database level for this.

Here we now however something extra: that the domain is a substring of the URL. So we can boost the filtering process by already doing some filtering at the database level:

domain = "www.example.com"
all_example_domain = [j for j in Journal.objects.filter(url__icontains=domain)
                        if j.domain == domain]

So here we already filter out Journal objects where the url does not contain the requested domain . It would technically be possible that there are still errors in it (for instance an url with www.example.com.com ). We thus better do a second filtering with j.domain == domain .

In case there is no such object, then the list will be empty, in case there are multiple, the list will contain two or more items.

We can use this to write a function that will do the proper filtering and raise errors in case no such object, or multiple objects are found. Like for instance:

from itertools import islice

def property_get(query_set, **kwargs):
    results = list(islice((j for j in qs
                          if all(getattr(j, k) == v for k, v in kwargs.items())),
                         2))
    if results:
        if len(results) > 1:
            raise MultipleObjectsReturned
        return results[0]    
    else:
        raise ObjectDoesNotExist

Which we can thus query with property_get(Journal.objects.all(), domain=domain) and it will raise an ObjectDoesNotExist or MultiObjectsReturned exceptions. But it is rather inefficient.

Usually it is better if you frequently query for a property, to store the property in the database as well as a field, and thus not use a property.

The property is accessible once you've created an instance of Journal .

Filtering the way you want will only allow filtering in python, but you should actually do this in SQL.

I suggest you add a domain field to your model, and remove the property. Consider using Django's signals.

    from django.db.models.signals import pre_save


    class Journal(models.Model):
        url = models.URLField(unique = True,blank=True)
        name = models.CharField(max_length=100)
        domain  = models.CharField(max_length=100)

    def extract_domain(sender, instance, **kwargs):
        instance.domain = urlparse(instance.url).netloc

   pre_save.connect(extract_domain, Journal)

And then you will able to .filter(domain=...) or .get(domain=) without a performance problem.

To do this exact thing, you don't need the database at all. So it works just like any other Python object in this case and you can ignore the fact it's a Django model.

url = "www.example.com"
jornal = Jornal(url=url)
print(jornal.domain)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM