풀스택 개발 공부로그

HTTPS

|

도메인이란 ?

인터넷에 연결되어있는 장치를 식별할 수 있는 주소를 ip라고 한다. ip는 숫자로 되어있는데 이를 이름으로 쉽게 기억하기 위해서 부여한것이 도메인이다.

호스트란?

네트워크에 연결되어있는 컴퓨터를 호스트라고 한다. 호스트 설정은 도메인을 호스트의 IP에 연결하는 행위이다.

네임서버

도메인에 해당하는 ip를 알려주는 서비스. WEBDIR :: DNS서버(네임서버)의 이해 How the DNS works - YouTube

HTTPS

네트워크 통신은 계층적으로 이루어져있다. Transport Layer(TCP/IP) , SSL/TLS, Application Layer(HTTP) HTTPS는 SSL프로토콜 위에서 돌아가는 HTTP를 말한다.

SSL 인증서

  1. 클라이언트가 접속한 서버가 신뢰 할 수 있는 서버임을 보장한다.
  2. SSL 통신에 사용할 공개키를 클라이언트에게 제공한다.

CA를 통해서 인증서 생성하는 방법 HTTPS와 SSL 인증서 - 생활코딩

웹서버 셋팅 HTTPS와 SSL 인증서 - 생활코딩

공개키

정보를 암호화하는데 사용하는 비밀번호 같은것을 키라고 한다. 대칭키라고 하면 암호화 하거나 복호화 할 때 사용하는 키가 같은것을 말한다. 하지만 이는 공개키를 알게되면 정보를 암호화해도 의미가 없어지게 된다. 이를 보완하기위해 나온것이 공개키이다. 공개키 방식은 두개의 키를 갖는다. 공개키와 비공개키. 공개키로 정보를 암호화하면 비공개키로 복호화할 수 있고, 비공개키로 암호화하면 공개키로 복호화할 수 있다. 비공개키는 자신만 가지고 있고 공개키를 타인에게 제공한다. 상대가 공개키로 정보를 암호화하여 나에게 정보를 보내면 나는 비공개키로 복호화할 수 있다. 공개키가 노출되더라도 복호화할 수 없기 때문에 안전하다.

Django Restframework nested serializer 연습

|

django restframework nested serializer (연습)

Serializers - Django REST framework 다음과 같은 model과 serializer가 있다.

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def __str__(self):
        return self.name

class Author(models.Model):
    name = models.CharField(max_length=200)
    email = models.EmailField()

    def __str__(self):
        return self.name

class Entry(models.Model):
    blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
    created_at = models.DateField(auto_now=True)
    updated_at = models.DateField(auto_now_add=True)
    authors = models.ManyToManyField(Author)

    class Meta():
        ordering = ['-created_at']
class Authorserialzier(serializers.ModelSerializer):
    class Meta:
        model = Author
        fields = '__all__'


class Entryserialzier(serializers.ModelSerializer):

    class Meta:
        model = Entry
        fields = '__all__'
        # depth = 1

FK를 사용하는 경우에 serializer의 meta에서 depth를 설정해주지 않으면, id값을 가져와서 출력해준다. 아무런 의미 없는 정보이다.

{
    "count": 1000,
    "next": "http://127.0.0.1:8000/tt/entry/?limit=2&offset=2",
    "previous": null,
    "results": [
        {
            "id": 33232,
            "created_at": "2019-11-13",
            "updated_at": "2019-11-13",
            "blog": 71,
            "authors": [
                51125,
                51128,
                51131,
                51132,
                51133
            ]
        },
        {
            "id": 33233,
            "created_at": "2019-11-13",
            "updated_at": "2019-11-13",
            "blog": 69,
            "authors": [
                51130,
                51131,
                51132
            ]
        }
    ]
}

해결방법은 depth값을 설정해주면된다.(참고 : Serializers - Django REST framework)
여기서는 한번만 들어가면 되니까 1로해준다. 여러번 파고들어가면 그에맞는 정수를 입력해주면 된다. depth = 1일 때 결과는 다음과 같다.

{
    "count": 1000,
    "next": "http://127.0.0.1:8000/tt/entry/?limit=2&offset=2",
    "previous": null,
    "results": [
        {
            "id": 33232,
            "created_at": "2019-11-13",
            "updated_at": "2019-11-13",
            "blog": {
                "id": 71,
                "name": "c",
                "tagline": ""
            },
            "authors": [
                {
                    "id": 51125,
                    "name": "0",
                    "email": "0@gmail.com"
                },
                {
                    "id": 51128,
                    "name": "3",
                    "email": "3@gmail.com"
                },
                {
                    "id": 51131,
                    "name": "6",
                    "email": "6@gmail.com"
                },
                {
                    "id": 51132,
                    "name": "7",
                    "email": "7@gmail.com"
                },
                {
                    "id": 51133,
                    "name": "8",
                    "email": "8@gmail.com"
                }
            ]
        },
        {
            "id": 33233,
            "created_at": "2019-11-13",
            "updated_at": "2019-11-13",
            "blog": {
                "id": 69,
                "name": "a",
                "tagline": ""
            },
            "authors": [
                {
                    "id": 51130,
                    "name": "5",
                    "email": "5@gmail.com"
                },
                {
                    "id": 51131,
                    "name": "6",
                    "email": "6@gmail.com"
                },
                {
                    "id": 51132,
                    "name": "7",
                    "email": "7@gmail.com"
                }
            ]
        }
    ]
}

Django ORM many to many save, retrieve

|

django orm many to many field create, save

Many-to-many relationships | Django documentation | Django

from django.db import models

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
>>> p1 = Publication(title='The Python Journal')
>>> p1.save()
>>> p2 = Publication(title='Science News')
>>> p2.save()
>>> p3 = Publication(title='Science Weekly')
>>> p3.save()
>>> a1 = Article(headline='Django lets you build Web apps easily')
>>> 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.

many to many 필드는 .add()로 추가해야한다. 하지만 저장하기 전에는 add()를 할 수 없다. 시도하면 위와같은 에러가 뜬다.

>>> a1.save()
>>> a1.publications.add(p1)

이렇게 해야한다.

>>> a2.publications.add(p1, p2)
>>> a2.publications.add(p3)
>>> a2.publications.add(p3)

객체를 풀어서 넣어줘야한다. 리스트로 넣어주면 인식을 못한다. 같은걸 두번 넣어도 두번 저장되지 않는다.

>>> p2.article_set.all()
<QuerySet [<Article: NASA uses Python>]>
>>> p1.article_set.all()
<QuerySet [<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>]>
>>> Publication.objects.get(id=4).article_set.all()
<QuerySet [<Article: NASA uses Python>]>

article에는 여러개의 publications가 있다. 그래서 article instance에서 바로 publication.all() 하면 publications의 리스트를 가져올 수 있다. 반대로 publications에 속하는 article을 가져오려면 뒤에 _set을 붙여줘야한다. 예를들어서 p1.article_set.all() 이렇식으로 할 수 있다. 아래코드로 연습했다.

from loremipsum import get_paragraph
import requests
import time
import random 

for i in Author._meta.fields:
    print(i)
print()
for i in Blog._meta.fields:
    print(i)
print()
for i in Entry._meta.fields:
    print(i)
print()

# word_site = "http://svnweb.freebsd.org/csrg/share/dict/words?view=co&content-type=text/plain"
# response = requests.get(word_site)
# words = response.content.splitlines()
# words = [x.decode('utf-8') for x in words]

#Authors 이름을 0~9로 10개의 객체 생성
l = []
for i in range(10):
    l.append(Author(name=i, email=f'{i}@gmail.com'))
Author.objects.bulk_create(l)

#Blog 는 a, b, c, d로 4개의 객체 생성
for i in ['a', 'b', 'c', 'd']:
    Blog(name=i).save()

    
#many to many관계에 있는 entry의 author
entry_authors = [set([random.randint(0, 9) for _ in range(random.randint(1, 9))]) for _ in range(1000)]


#랜덤으로 만든 authors set의 list로 entry만듬 
for i in range(10000):
    temp = Entry(blog = b[random.randint(0, 3)])
    temp.save()
    temp.authors.add(*[a[x] for x in entry_authors[random.randint(0, 999)]])
    
b = Blog.objects.all()
e = Entry.objects.all()
a = Author.objects.all()

print(Author.objects.all())
print(Blog.objects.all())
print(Entry.objects.all())


# b.delete()
# e.delete()
# a.delete()
>>> e[0].authors.all()
<QuerySet [<Author: 0>, <Author: 2>]>
>>> len(a[i].entry_set.all())
<QuerySet [<Entry: Entry object (2158)>, <Entry: Entry object (2165)>, <Entry: Entry object (2168)>, <Entry: Entry object (2172)>, <Entry: Entry object (2173)>, <Entry: Entry object (2175)>, <Entry: Entry object (2176)>, <Entry: Entry object (2180)>, <Entry: Entry object (2182)>, <Entry: Entry object (2184)>, <Entry: Entry object (2185)>, <Entry: Entry object (2186)>, <Entry: Entry object (2187)>, <Entry: Entry object (2188)>, <Entry: Entry object (2191)>, <Entry: Entry object (2197)>, <Entry: Entry object (2199)>, <Entry: Entry object (2200)>, <Entry: Entry object (2201)>, <Entry: Entry object (2203)>, '...(remaining elements truncated)...']>

How Django knows to UPDATE vs. INSERT(bulk save, update)

|

How Django knows to UPDATE vs. INSERT

Model instance reference | Django documentation | Django

You may have noticed Django database objects use the same save() method for creating and changing objects. Django abstracts the need to use INSERT or UPDATE SQL statements. Specifically, when you call save(), Django follows this algorithm:

객체를 생성하거나 변경할 때 둘 다 save()함수를 사용한다. 장고는 insert와 update를 추상화 할 필요가 있다.

If the object’s primary key attribute is set to a value that evaluates to True (i.e., a value other than None or the empty string), Django executes an UPDATE.

객체의 PK값이 true로 인식되면 update라고 판단한다.

If the object’s primary key attribute is not set or if the UPDATE didn’t update anything (e.g. if primary key is set to a value that doesn’t exist in the database), Django executes an INSERT.

The one gotcha here is that you should be careful not to specify a primary-key value explicitly when saving new objects, if you cannot guarantee the primary-key value is unused. For more on this nuance, see Explicitly specifying auto-primary-key values above and Forcing an INSERT or UPDATE below.

여기서 중요한 점은 객체를 저장할 때 PK값을 명시적으로 하면 안된다는것이다. pk값이 사용되지 않는다는걸 보장할 수 없다면. -> pk값이 데이터베이스에 없으면 insert, 이미 있는것이면 update

In Django 1.5 and earlier, Django did a SELECT when the primary key attribute was set. If the SELECT found a row, then Django did an UPDATE, otherwise it did an INSERT. The old algorithm results in one more query in the UPDATE case. There are some rare cases where the database doesn’t report that a row was updated even if the database contains a row for the object’s primary key value. An example is the PostgreSQL ON UPDATE trigger which returns NULL. In such cases it is possible to revert to the old algorithm by setting the select_on_save option to True.

saving objects

Model instance reference | Django documentation | Django

Explicitly specifying auto-primary-key values

If a model has an AutoField but you want to define a new object’s ID explicitly when saving, just define it explicitly before saving, rather than relying on the auto-assignment of the ID:

>> b3 = Blog(id=3, name='Cheddar Talk', tagline='Thoughts on cheese.')
>> b3.id     # Returns 3.
>> b3.save()
>> b3.id     # Returns 3.

If you assign auto-primary-key values manually, make sure not to use an already-existing primary-key value! If you create a new object with an explicit primary-key value that already exists in the database, Django will assume you’re changing the existing record rather than creating a new one.

authofield인 경우에도 명시적으로 id값을 할당하여 pk값을 지정할 수 있다. 주의해야 할 점은 데이터베이스에 이미 있는 pk값이면 수정하게되므로 조심해야한다.

Updating attributes based on existing fields

Sometimes you’ll need to perform a simple arithmetic task on a field, such as incrementing or decrementing the current value. The obvious way to achieve this is to do something like:

>> product = Product.objects.get(name='Venezuelan Beaver Cheese')
>> product.number_sold += 1
>> product.save()

The process can be made robust, avoiding a race condition , as well as slightly faster by expressing the update relative to the original field value, rather than as an explicit assignment of a new value. Django provides F expressions for performing this kind of relative update. Using F expressions , the previous example is expressed as:

Specifying which fields to save

If save() is passed a list of field names in keyword argument update_fields, only the fields named in that list will be updated. This may be desirable if you want to update just one or a few fields on an object. There will be a slight performance benefit from preventing all of the model fields from being updated in the database. For example: 원하는 필드만 골라서 업데이트할 수 있다. save()에 인자를 전달해서.

product.name = 'Name changed again'
product.save(update_fields=['name'])

효율적인 insert와 update를 위한 QuerySet API reference, 효율적인 insert와 update를 위한

QuerySet API reference | Django documentation | Django

bulk_create(),

QuerySet API reference | Django documentation | Django

This method inserts the provided list of objects into the database in an efficient manner (generally only 1 query, no matter how many objects there are): 객체 리스트를 한번에 저장하는 효율적인 방법

>>> Entry.objects.bulk_create([
...     Entry(headline='This is a test'),
...     Entry(headline='This is only a test'),
... ])

bulk_update()

QuerySet API reference | Django documentation | Django

>>> objs = [
...    Entry.objects.create(headline='Entry 1'),
...    Entry.objects.create(headline='Entry 2'),
... ]
>>> objs[0].headline = 'This is entry 1'
>>> objs[1].headline = 'This is entry 2'
>>> Entry.objects.bulk_update(objs, ['headline'])	
s = time.time()
for i in words:
    Author(name=i, email=f'{i}@gmail.com').save()    
print('무식한 방법', time.time() - s, '초')
Author.objects.all().delete()

s = time.time()
l = []
for i in words:
    l.append(Author(name=i, email=f'{i}@gmail.com'))
Author.objects.bulk_create(l)
print('똑똑한 방법', time.time() - s, '초')

>>> 무식한 방법 25.767067193984985 
>>> 똑똑한 방법 0.6707980632781982 

Django ORM retrieve

|

Django ORM (retrieve)

Making queries | Django documentation | Django

To retrieve objects from your database, construct a QuerySet via a Manager on your model class. 디비에서 객체를 가져오려면 모델의 manager를 통해 queryset을 만들면 된다.

A QuerySet represents a collection of objects from your database. It can have zero, one or many filters. Filters narrow down the query results based on the given parameters. In SQL terms, a QuerySet equates to a SELECT statement, and a filter is a limiting clause such as WHERE or LIMIT.

데이터베이스에서 가져온 객체들의 모음을 표현하는것이 queryset이다. 0개부터 여러개의 filter를 가질 수 있다. 필터는 주어진 파라미터들을 통해 쿼리 결과를 좁혀나간다. queryset은 selete이고, 필터는 where, limit절이라고 생각하면된다.

You get a QuerySet by using your model’s Manager . Each model has at least one Manager , and it’s called objects by default. Access it directly via the model class, like so:

각 모델마나 하나 이상의 manager를 가지고 있다. 이름은 기본값으로 objects이다. 모델클레스를 통해 바로 접근할 수 있다.

>>> Blog.objects
<django.db.models.manager.Manager object at ...>
>>> b = Blog(name='Foo', tagline='Bar')
>>> b.objects
Traceback:
    ...
AttributeError: "Manager isn't accessible via Blog instances."

Managers are accessible only via model classes, rather than from model instances, to enforce a separation between “table-level” operations and “record-level” operations. managers는 모델 인스턴스가 아닌 모델 클래스를 통해서만 접근할 수 있다. tabel level의 수행과 record level의 수행을 구분하기 위함이다.

Retrieving specific objects with filters

The QuerySet returned by all() describes all objects in the database table. Usually, though, you’ll need to select only a subset of the complete set of objects. To create such a subset, you refine the initial QuerySet , adding filter conditions. The two most common ways to refine a QuerySet are:

filter(kwargs) Returns a new QuerySet containing objects that match the given lookup parameters.

exclude(kwargs) Returns a new QuerySet containing objects that do not match the given lookup parameters.

The lookup parameters (kwargs in the above function definitions) should be in the format described in Field lookups below. loopup 파라미터는 다음과 같은 형식이어야 합니다.

Field lookups are how you specify the meat of an SQL WHERE clause. They’re specified as keyword arguments to the QuerySet methods filter() , exclude() and get() .

field lookups는 sql where를 어떻게 구체화 해야하는지에 대한 표준이다. queryset의 filter, exclude, get과 같은 함수에 키워드 인자로 구체화한다.

Basic lookups keyword arguments take the form field__lookuptype=value. (That’s a double-underscore). For example:

Entry.objects.filter(pub_date__lte='2006-01-01')

translates (roughly) into the following SQL
SELECT * FROM blog_entry WHERE pub_date <= ‘2006-01-01’;

The field specified in a lookup has to be the name of a model field. There’s one exception though, in case of a ForeignKey you can specify the field name suffixed with _id. In this case, the value parameter is expected to contain the raw value of the foreign model’s primary key. For example: 룩업에 명시된 필드는 모델 필드의 이름이어야 한다. 하나의 예외가 있는게 FK인 경우이다. 접두사로 ‘_id’를 붙인다. 이런 경우에는 외래 모델의 프라이머리키의 raw값을 기대한다.

Entry.objects.filter(blog_id=4)
>>> Blog.objects.get(id__exact=14)  # Explicit form
>>> Blog.objects.get(id=14)         # __exact is implied
>>> Blog.objects.get(name__iexact="beatles blog")

iexact : A case-insensitive match. So, the query
Would match a Blog titled ”Beatles Blog”, ”beatles blog”, or even ”BeAtlES blOG”.

Lookups that span relationships

Making queries | Django documentation | Django

Django offers a powerful and intuitive way to “follow” relationships in lookups, taking care of the SQL JOINs for you automatically, behind the scenes. To span a relationship, just use the field name of related fields across models, separated by double underscores, until you get to the field you want.

장고에서는 SQL의 join기능인 관계된 모델들의 필드를 추적하는 강력하고 직관적인 방법이 있다.

This example retrieves all Entry objects with a Blog whose name is ’Beatles Blog’:

Entry.objects.filter(blog__name='Beatles Blog')

If you are filtering across multiple relationships and one of the intermediate models doesn’t have a value that meets the filter condition, Django will treat it as if there is an empty (all values are NULL), but valid, object there. All this means is that no error will be raised. For example, in this filter:

여러 관계들을 거쳐가며 필터링한 후 결과가 비어있다면 null값을 반환한다. 하지만 유효한 값이다. 에러를 발생시키지 않는다.