모듈 임포트 후 메서드 자동 완성 안 되는 문제 해결
모델 클래스를 임포트한 후 objects와 같은 메서드가 자동 완성되지 않는 경우, 모델 클래스 내부에 objects = models.Manager()를 추가하면 해결됩니다.
단일 테이블 데이터 삽입
데이터 삽입은 세 가지 방식으로 가능합니다: save(), create(), bulk_create(). 두 번째 방식인 create()는 저장과 동시에 객체를 반환하며, 반환된 객체는 삽입된 필드 값을 포함합니다.
from my_app.models import Book
def orm_add(request):
# 방법 1: 인스턴스 생성 후 save()
new_book = Book(title="go 책", price=100, pub_date="2021-01-03", publish="인민출판사")
new_book.validate_unique() # 중복 여부 검사
print(new_book.title)
new_book.save()
# 방법 2: create() 사용 (반환값 존재)
book_obj = Book.objects.create(title="자바", price=300, pub_date="2021-04-03", publish="난징출판사")
print(book_obj.title)
return HttpResponse("데이터 추가 완료")
bulk_create()는 대량 삽입에 유리합니다. 그러나 중복 키 오류 발생 시 예외 처리가 필요합니다.
book_list = []
for i in range(1, 100):
bk = models.Test.objects.create(name=f"파이썬-{i}", price=i)
book_list.append(bk)
try:
models.Test.objects.bulk_create(book_list)
except Exception:
pass # 중복 시 무시
사용자 정의 삽입 로직 구현
이미지 필드(ImageField)의 경로를 동적으로 설정하고, 이를 바탕으로 다른 필드(licence_path)를 자동 생성하는 경우, save() 메서드를 오버라이드해야 합니다.
# model.py
def user_directory_path(instance, filename):
return os.path.join("upload", instance.company.username, filename)
class CompanyAuth(models.Model):
company = models.ForeignKey("Company", on_delete=models.CASCADE, to_field="id")
title = models.CharField(max_length=64, verbose_name="기업명")
uniques_id = models.CharField(max_length=64, verbose_name="사업자등록번호")
leader = models.CharField(max_length=10, verbose_name="대표자명")
licence_path = models.CharField(max_length=64, verbose_name="영업허가서 경로")
leader_identify = models.CharField(max_length=10, verbose_name="직위")
avatar_img = models.ImageField(upload_to=user_directory_path, default="default_upload/test.jpg", blank=True, null=True)
def save(self, *args, **kwargs):
# 이미지 경로를 기반으로 라이선스 경로 생성
path = user_directory_path(self, self.avatar_img.name)
self.licence_path = os.path.join(settings.MEDIA_URL, path)
super().save(*args, **kwargs)
중복 파일 처리 전략
django.core.files.storage.default_storage는 중복 파일을 자동으로 식별하고 이름을 무작위로 재설정하여 저장합니다. 주요 메서드는 다음과 같습니다:
save(name, content): 파일 저장. 중복 시 자동 이름 변경.get_available_name(name): 중복 여부 확인 후 고유 이름 반환.url(name): 공개 접근용 URL 생성.exists(name): 파일 존재 여부 확인.delete(name): 파일 삭제.
from django.core.files.storage import default_storage
from django.core.files.base import ContentFile
content = b"Hello, World!"
file = ContentFile(content)
saved_path = default_storage.save("test.txt", file) # 중복 시 자동 이름 변경
file_url = default_storage.url(saved_path) # http://host/media/test_1.txt
단일 테이블 조회
쿼리 결과는 항상 QuerySet 형태이며, 리스트처럼 반복 가능합니다. 출력을 명확히 하기 위해 __str__ 메서드를 정의하는 것이 좋습니다.
values()와 values_list()의 차이점:
values(): 딕셔너리 형태로 반환.values_list(): 튜플 형태로 반환.
조건 기반 조회
contains: 포함 여부 확인. 예:Student.objects.filter(name__contains='화').startswith,endswith: 시작/끝 문자열 일치.i접두사로 대소문자 무시 가능.isnull: NULL 값 여부 확인.in,range: 범위 내 포함 여부. 예:age__range=(20, 30).gt,gte,lt,lte: 비교 연산.year,month,day등: 날짜/시간 필드 분리.
F 쿼리와 Q 쿼리
F 쿼리
데이터베이스 내 두 컬럼 간 비교에 사용됩니다.
from django.db.models import F
# 생성 시간과 수정 시간이 동일하지 않은 항목 추출
students = Student.objects.exclude(created_time=F('updated_time'))
# 필드 값 증가
models.Book.objects.update(comment=F('comment') + 1)
# 별칭 지정
ret = models.NaviBar.objects.filter(rooter=pk).annotate(menu_title=F('rooter__title')).values('title', 'name', 'is_menu', 'menu_title')
Q 쿼리
논리 조합을 위한 도구입니다. & (AND), | (OR), ~ (NOT).
from django.db.models import Q
# 나이가 19 미만 또는 20 초과
student_list = Student.objects.filter(Q(age__lt=19) | Q(age__gt=20))
# NOT 조건
Student.objects.filter(~Q(pk=30))
집계 및 그룹화 쿼리
관련 내용은 여기에서 확인하세요.
원시 SQL 실행
ORM을 우회하여 직접 SQL을 실행할 수 있습니다. 보안을 위해 파라미터화된 방식을 사용해야 합니다.
# raw() 사용
res = models.Author.objects.raw('SELECT * FROM app01_author WHERE nid > %s', [1])
# connections 사용
from django.db import connections
with connections['default'].cursor() as cursor:
cursor.execute("UPDATE TbEmp SET sal=sal+10 WHERE dno=30")
cursor.execute("SELECT ename, job FROM TbEmp WHERE dno=10")
rows = cursor.fetchall()
보안 주의사항
SQL 인젝션 방지를 위해 문자열 결합은 피하고, 파라미터화된 쿼리를 사용하세요.
sql = "SELECT id, name FROM student WHERE id = %s"
cursor.execute(sql, [user_id])
result = cursor.fetchall()
단일 테이블 삭제
obj.delete(): 단일 객체 삭제.queryset.delete(): 여러 객체 삭제. 반환값은 (삭제 건수, 테이블 정보) 형식.
Book.objects.filter(title="go").delete()
단일 테이블 수정
update()는 효율적이며, save()는 모든 필드를 다시 저장하므로 권장하지 않습니다.
Book.objects.filter(price=100).update(title="전체 수정")
데이터베이스 마이그레이션 오류 해결
테이블에 컬럼 없음
오류: Unknown column 'name' in 'django_content_type'
해결: 수동으로 컬럼 추가
ALTER TABLE django_content_type ADD COLUMN name VARCHAR(10);
테이블 중복 생성
오류: Table 'echart_show_category' already exists
해결: 초기화 스킵
python manage.py migrate --fake-initial