Project : 오설록 클론 - Takeaways

JunePyo Suh·2020년 6월 7일
0

Backend API collection for the project:
https://documenter.getpostman.com/view/11374945/SztK2Qqr

Query, Query String, Dictionary Unpacking

Unless input query string is a list, use request.GET.get() and split(',')

If multiple string values are accessed with request.GET.getlist() command, those strings will be stored as a single element in a list. To be more specific, ?fruit=orange,apple,grape will be stored in the following form:
['orange,apple,grape']
In this case, use request.GET.get() function .split(',') to store the values as separate string elements in a list.

Q() object

Dynamically compose an OR query filter in Djago

Q() query chaning can be made possible with |=.

values = [1,2,3]

// Turn list of values into list of Q objects
queries = [Q(pk=value) for value in values]

// Take one Q object from the list
query = queries.pop()

// Or the Q object with the ones remaining in the list
for item in queries:
    query |= item

// Query the model
Article.objects.filter(query)

My Q() syntax used for the project:

if packs:
            print(packs)
            queries = [Q(title__icontains=pack) if pack != '파우더' else Q(fourth_category__name = pack) for pack in packs]
            print(queries)
            pack_q = queries.pop()
            for each_q in queries:
                print(each_q)
                print(type(each_q))
                pack_q |= each_q
            print(pack_q)
            print(Item.objects.filter(pack_q))

This is what is happening inside the loop, according to print() in the code above:

['잎차', '피라미드']
[<Q: (AND: ('title__icontains', '잎차'))>, <Q: (AND: ('title__icontains', '피라미드'))>]
(AND: ('title__icontains', '잎차'))
<class 'django.db.models.query_utils.Q'>
(OR: ('title__icontains', '피라미드'), ('title__icontains', '잎차'))

Field Lookups

Field lookups in Django lookups spice up the meat of an SQL WHERE clause. Lookups can be specified as keyword arguments to the QuerySet methods filter(), exclude() and get().

As a convenience when no lookup type is provided (like in Entry.objects.get(id=14)) the lookup type is assumed to be exact.

Exclude() : Django's in / not in query

The exclude function works like the "not in" query in SQL.

table1.objects.exclude(id__in=
       table2.objects.filter(your_condition).values_list('id', flat=True))

Order_by()

order_by()
order_by(*fields)
By default, results returned by a QuerySet are ordered by the ordering tuple given by the ordering option in the model’s Meta. You can override this on a per-QuerySet basis by using the order_by method.

Example:

Article.objects.filter(pub_date__year=2005).order_by('-pub_date', 'headline')

The result above will be ordered by pub_date descending, then by headline ascending. The negative sign in front of "-pub_date" indicates descending order. Ascending order is implied. To order randomly, use "?", like so:

Article.objects.order_by('?')

order_by('?') queries may be expensive and slow, depending on the database backend you’re using.

To order by a field in a different model, use the same syntax as when you are querying across model relations. That is, the name of the field, followed by a double underscore (__), followed by the name of the field in the new model, and so on for as many models as you want to join. For example:

Article.objects.order_by('blog__name', 'headline')

When ordering by a field that is a relation to another model, Django will impose default ordering specified in Meta.ordering, and if it does not exist, order by the related model’s primary key.

Article.objects.order_by('blog')

is identical to:

Article.objects.order_by('blog__id')

If Blog had ordering = ['name'], then the first queryset would be identical to:

Article.objects.order_by('blog__name')

Ordering by query expressions (calling asc() or desc() on the expression) is also possible:

Article.objects.order_by(Coalesce('summary', 'headline').desc())

There is no default ordering of a list when RDBMS returns rows that meet the conditions of a provided select statement

Though one can add a default ordering on the Django level, which will require an order_by clause to every non-ordered query,

class Table(models.Model):
    ...
    class Meta:
        ordering = ['name']

such ordering may be a performance drag. If there is a reason to get an ordered & filtered list, apply order_by function after calling the filter() function.

Annotate()

Annotates each object in the QuerySet with the provided list of query expressions. An expression may be a simple value, a reference to a field on the model (or any related models), or an aggregate expression (averages, sums, etc.) that has been computed over the objects that are related to the objects in the QuerySet.

Each argument to annotate() is an annotation that will be added to each object in the QuerySet that is returned.
To get the number products for each category:

categories = Category.objects.annotate(Count('product'))

This adds the <field_name>__count attribute to each instance returned:

categories.values_list('name', 'product__count')
[('Clothing', 42), ('Footwear', 12), ...]

Or provide a custom name for your attribute by using a keyword argument:

categories = Category.objects.annotate(num_products=Count('product'))

Annotated fields can be used in querysets:

>>> categories.filter(num_products__gt=20)

Aggregate()

Aggregate() enables us to retrieve values that are derived by summarizing or aggregating a collection of objects.

from django.db.models import Avg, Max, Min
>>> Book.objects.aggregate(Avg('price'))
{'price__avg': 34.35}

Aggregate() is a terminal clause for a QuerySet that, when invoked, returns a dictionary of name-value pairs. The name is an identifier for the aggregate value; the value is the computed aggregate. The auto-generated name comes from the name of the field and the aggregate function. You can also manually specify a name for the aggregate value.

Ordering by number of reverse ForeignKey matches

I had to implement sorting items (tea products) based on number of reviews written and number of orders placed on the item, both (reviews and orders) of which were in reverse ForeignKey relationships with the class Item.

from django.db.models import Count

'review'        : list(item_qs.annotate(count=Count('itemreview')).order_by('-count')),
'popular'       : list(item_qs.annotate(count=Count('order')).order_by('-count'))

Even though no separate related_name field options were declared in ForeignKeys of ItemReview Class and Order Class, lowercased class name should be used inside of Count expression instead of _set syntax, according to the Django documentation.
classname_set syntax is used when accessing a ManyToOne Django Manager from an object instance. In other cases (query expressions, etc.) omit the _set syntax, unless related name is declared in _set form.

Django SUM query

from django.db.models import Sum
ModelName.objects.aggregate(Sum('field_name'))

If you would like to get the sum of fields with null values,

ModelName.objects.filter(field_name__isnull=True).aggregate(Sum('field_name'))

Update()

Update() function is effective if used along with F() object.

OrderItem.objects.filter(id=orderitem_id).update(quantity=F('quantity')+delta)

An F() object represents the value of a model field or an annotated column. With the help of F() objects, referencing model field values or performing database operations can be facilitated without actually having to pull data out of the database into Python memory.

For example,

writer = Writer.objects.get(name='June')
writer.stories_filed += 1
writer.save()

can be done the following way:

from django.db.models import F

writer = Writer.objects.get(name='June')
writer.stories_filed = F('stories_filed') + 1
writer.save()

Delete()

delete from table_name where id = 1;

is equivalent to:

SomeModel.objects.filter(id=id).delete()

You can also delete from an instance:

instance = SomeModel.objects.get(id=id)
instance.delete()

How can I filter a Django query with a list of values?

Use id__in

item_ids = request.GET.getlist('ids')
cart = OrderItem.objects.filter(id__in=[item_ids])

id__in is very useful when there is a need to filter for selected number of items

Passing a QueryDict to a Django QuerySet

request.GET would result in some sort of QueryDict, such as

q = QueryDict('name=Alex&lastname=Lee')

This can be passed as an argument to filter() function like the following:

People.objects.filter(**q.dict())

Dictionary unpacking will have the same result as:

People.objects.filter(name='John', lastname='Smith')

This is how a QueryDict is formatted:

>>> QueryDict('a=1&a=2&c=3')
<QueryDict: {'a': ['1', '2'], 'c':['3']}>

More on Dictionary Unpacking

category = request.GET.get('category', None)
tag = request.GET.get('tag', None)
item = request.GET.get('item', None)
data = {}

if category:
  data['category__name'] = category

if tag:
  data['tag__name'] = tag

if item:
  data['item__name'] = item

Product.objects.filter(**data).order_by()...

Django string to date format

s = "2014-04-07"
datetime.datetime.strptime(s, "%Y-%m-%d").date()
datetime.date(2014, 4, 7)

datetime parsing --> can date in string be accepted by Django date field?

Originally, I thought it wouldn't work, so used:

from django.utils.dateparse import parse_datetime
parse_datetime('2016-10-03T19:00:00')

But I figured that as long as the string comes in the following format:
yyyy-mm-dd, django automatically parses it as an appropriate value for datefield. So, no need for parse_datetime() in this case

How to update fields in a model without creating a new record in django?

t = TemperatureData.objects.get(id=1)
t.value = 999 # change field
t.save() # this will update only

Cacheing with Queryset

To enable cache in QuerySet, simply save the QuerySet in a variable and reuse it. Django QuerySet class has a _result_cache variable where it saves the query results (Django models) in list . _result_cache is None if QuerySet does not have any cache, otherwise a list of model objects.

What is the right way to set the default value of a many2many field?

Initialize data with fixtures

각각 다른 relationship 에서 access and create 는 어떻게 하는지 확실히 알기!!!!
OnetoOne은 바로접근? manager 가 없나
Relationship initialization:
OneToOneField:

p3 = Place(name='Demon Dogs', address='944 W. Fullerton')
>>> Restaurant.objects.create(place=p3, serves_hot_dogs=True, serves_pizza=False)

ManyToManyField:

class Publication(models.Model):
    title = models.CharField(max_length=30)

    class Meta:
        ordering = ['title']

    def __str__(self):
        return self.title

class Article(models.Model):
    headline = models.CharField(max_length=100)
    publications = models.ManyToManyField(Publication)

    class Meta:
        ordering = ['headline']

    def __str__(self):
        return self.headline

>>> a1 = Article(headline='Django lets you build Web apps easily')
// You can’t associate it with a Publication until it’s been saved:
>>> a1.publications.add(p1)
Traceback (most recent call last):
...
ValueError: "<Article: Django lets you build Web apps easily>" needs to have a value for field "id" before this many-to-many relationship can be used.

>>> a1.save()
>>> a1.publications.add(p1)
// Adding a second time is OK, it will not duplicate the relation:
>>> a1.publications.add(p1)

// Or, create and add a Publication to an Article in one step using create():
>>> new_publication = a2.publications.create(title='Highlights for Children')

Python

Ternary conditional operator

syntax

a if condition else b

Both if and else expressions are required, as only using one of the two wil lresult in an error.

>>> a = 5 if True
  File "<stdin>", line 1
    a = 5 if True
                ^
SyntaxError: invalid syntax

>>> a = 5 if True else 2

Conditionals are an expression, not a statement.Therefore, we cannot use assignment statements or 'pass' or other statements within a conditional expression

>>> pass if False else x = 5
  File "<stdin>", line 1
    pass if False else x = 5
          ^
SyntaxError: invalid syntax

A variable can be assigned, however, in this way:

x = a if True else b

python split()

txt = "hello, my name is Peter, I am 26 years old"

x = txt.split(",")

print(x)

['hello', ' my name is Peter', ' I am 26 years old']

On the other hand,
x = txt.split(", ")

print(x)

['hello', 'my name is Peter', 'I am 26 years old']
Likewise, splitting with no space but just comma like above would yield split strings with extra space (due to original spacing between elements)

formatting decimal number places

When it comes to float numbers, you can use format specifiers:

f'{value:{width}.{precision}}'
where:

Value is any expression that evaluates to a number.
Width specifies the number of characters used in total to display, but if value needs more space than the width specifies then the additional space is used.
Precision indicates the number of characters used after the decimal point.

>>> a = 10.1234
>>> f'{a:.2f}'
'10.12'

Error Handling

User sign-in

INCORRECT_PASSWORD as a JsonResponse message may be too detaied an error message. Simply return a HttpResponse with a status code of 401 in this case.

Reverse accessor clash

Happens when more than two reverse relationships reference back to the same object with the same _set call.
This error can be corrected by setting different related_name options for relationships, or blocking reverse access with related_name='+'

Lazy reference error

Matching Query doesn't exist

This error happens when you try to access a non-existing object.
Possible solutions:
1. get_object_or_404(Object, pk=option) : users will get a 404 meaning that they are trying to access a non-existing resource.
2. This may be the case when you have created pre-designed models but haven't read in the data yet. Read in the data, and whatever you were trying to filter or retrieve with query expression may start working.

Function object has no attribute 'as_view'

Make sure you are applying as_view() to a class based view.
Also make sure you are not omitting parenthesis after as_view (as_view(), not as_view)

Django cannot resolve keyword 'Word' into field. Choices are:

This error occurs when the keyword you are trying to use to access foreign_key related models or many_to_many related models is not right. Double check whether you are calling correct related_name, or whether you should be adding _set or not after the class name in reverse reference cases.

DateTimeField(auto_now_add=True) not working

You can't set auto_now_add=True and specify a default at the same time. The problem is that Django needs to know what value to use for your existing entries in the database.
You can either set null=True, then the value will be left as None.

created_time = models.DateTimeField('Created Time', auto_now_add=True, null=True)

Or, simply remove the default, and run makemigrations again.

created_time = models.DateTimeField('Created Time', auto_now_add=True)

When Django prompts you, select Option 1), and specify a default (e.g. timezone.now).

0개의 댓글