Django raw sql вставить запрос в тройной кавычке: Django интерпретирует нулевые значения из ajax данных запроса как столбец None - PullRequest
0 голосов
/ 18 марта 2020

Я работаю над проектом Django / React. Используя DRF, я создаю маршрут API для выполнения SQL запросов в моей базе данных PostgreSQL. Но у меня возникают проблемы с моей текущей настройкой кода.

Я настроил свой запрос INSERT в API как raw queries (using cursor), заключенный в многострочную строку из тройных кавычек """INSERT...""", затем отформатировал мои значения, используя форматирование строки %s. В моем API я записываю все данные из тела запроса в переменную. Все работает нормально, если все данные запроса заполнены. Но если это null, Django, очевидно, присваивает None переменной.

Теперь вернемся к моему sql запросу, Django будет обрабатывать ноль %s как None и как table column вместо правильного нулевого значения, таким образом выбрасывая ProgrammingError column "none" does not exist.

Вот примеры кодов:

Реагировать Интерфейс

const [lastName, setLastName] = useState('')
const [firstName, setFirstName] = useState('')
const [middleName, setMiddleName] = useState('')
const [nameExtn, setNameExtn] = useState('')
const [sex, setSex] = useState('')
const [civilStatus, setCivilStatus] = useState('')
const [bloodType, setBloodType] = useState('')
const [height, setHeight] = useState('')
const [weight, setWeight] = useState('')

const newPersonalInfo = (token, data) => {
    let endpoint = "/jobnet/api/profile/pds/basic/personal/"
    let lookupOptions = {
        method: "POST",
        headers: {
          'Content-Type': 'application/json',
            'Authorization': `Token ${token}`
        },
        body: JSON.stringify(data),
        credentials: 'include'
      }
    fetch(endpoint, lookupOptions)
    .then(res=>res.json())
    .then(function(info){
     console.log(info)
    })
    .catch(err=>console.log(err));
}

const handleNewPersonalInfo = () => {
    newPersonalInfo(props.user.token, {
        firstname: firstName,
        lastname: lastName,
        middlename: middleName,
        extension: nameExtn,
        birthdate: selectedDate,
        sex: sex,
        civilstatus: civilStatus,
        bloodtype: bloodType,
        height: height,
        weight: weight,
    })
}

...

return(
    <Button
        variant="contained"
        color="primary"
        onClick={handleNewPersonalInfo}
    >
        SAVE
    </Button>
)

Django API (DRF)

class APIListCreate__PersonalInfo(generics.ListCreateAPIView):
    try:
        serializer_class = PDSBasicPersonalInfoSerializer
        permission_classes  = (jobnet_permissions.IsAuthenticated,)
        authentication_classes = (knox_TokenAuthentication,)
    except Exception as e:
        traceback.print_exc()

    def get_queryset(self):
        user = self.request.user
        if user and user.is_authenticated:
            query = ("""
                SELECT
                    bsinfo.firstname,
                    bsinfo.middlename,
                    bsinfo.surname,
                    bsinfo.birthdate,
                    bsinfo.sex,
                    bsinfo.extention,
                    bsinfo.civilstatus,
                    bsinfo.height_m,
                    bsinfo.weight_kg,
                    bsinfo.bloodtype,
                    bsinfo.photo_path
                FROM jobnet_app.basicinfo bsinfo
                WHERE
                id=%s
            """ % user.id)
            return raw_sql_select(query, "default")
        else:
            return None

    def get(self, request):
        data = [
            {
                "first_name": col.firstname,
                "middle_name": col.middlename,
                "last_name": col.surname,
                "name_extension": col.extention,
                "birthdate": col.birthdate,
                "sex": col.sex,
                "civil_status": col.civilstatus,
                "height": col.height_m,
                "weight": col.weight_kg,
                "blood_type": col.bloodtype,
                "photo_path": col.photo_path
            } for col in self.get_queryset()[1]
        ]
        return Response(data[0])

    def post(self, request):
        try:
            user = request.user.id
            firstname = request.data.get('firstname') or ''
            middlename = request.data.get('middlename') or ''
            lastname = request.data.get('lastname') or ''
            birthdate = request.data.get('birthdate') or ''
            sex = request.data.get('sex') or ''
            extension = request.data.get('extension') or ''
            civilstatus = request.data.get('civilstatus') or ''
            height_m = request.data.get('height') or 0
            weight_kg = request.data.get('weight') or 0
            bloodtype = request.data.get('bloodtype') or ''
            query = ("""
                START TRANSACTION;
                    INSERT INTO jobnet_app.basicinfo (
                        id,
                        firstname,
                        middlename,
                        surname,
                        birthdate,
                        sex,
                        extention,
                        civilstatus,
                        height_m,
                        bloodtype,
                        weight_kg
                    )
                    VALUES (%s,'%s','%s','%s','%s','%s','%s','%s',%s,'%s',%s);
            """ % (
                    user,
                    firstname,
                    middlename,
                    lastname,
                    birthdate,
                    sex,
                    extension,
                    civilstatus,
                    height_m,
                    bloodtype,
                    weight_kg
                )
            )
            unformatted_query_result = raw_sql_insert(query, "default")
            if unformatted_query_result:
                raw_sql_commit("default")
                return Response({
                    "success": True,
                    "message": "Your basic personal information has been updated successfully."
                }, status=status.HTTP_201_CREATED)
            else:
                raw_sql_rollback("default")
                return Response({
                    "success": False,
                    "message": "There was a problem updating your personal information."
                }, status=status.HTTP_400_BAD_REQUEST)
        except Exception as e:
            traceback.print_exc()
            return Response({
                "success": False,
                "message":"Internal System Error: " + str(e)
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

При указанной выше настройке я получаю такую ​​ошибку:

Traceback (most recent call last):
  File "C:\Users\Acer\Envs\adnwebsite-react\lib\site-packages\django\db\backends\utils.py", line 83, in _execute
    return self.cursor.execute(sql)
psycopg2.ProgrammingError: column "none" does not exist
LINE 4: ...on','2002-03-18T06:18:45.284Z','Male','','Single',None,'B+',...
                                                             ^


The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "D:\PGADN\Projects\pgadn-v2-website\adnwebsite\reactify\utils.py", line 17, in raw_sql_insert
    cn.execute(query)
  File "C:\Users\Acer\Envs\adnwebsite-react\lib\site-packages\django\db\backends\utils.py", line 100, in execute
    return super().execute(sql, params)
  File "C:\Users\Acer\Envs\adnwebsite-react\lib\site-packages\django\db\backends\utils.py", line 68, in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
  File "C:\Users\Acer\Envs\adnwebsite-react\lib\site-packages\django\db\backends\utils.py", line 77, in _execute_with_wrappers
    return executor(sql, params, many, context)
  File "C:\Users\Acer\Envs\adnwebsite-react\lib\site-packages\django\db\backends\utils.py", line 85, in _execute
    return self.cursor.execute(sql, params)
  File "C:\Users\Acer\Envs\adnwebsite-react\lib\site-packages\django\db\utils.py", line 89, in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
  File "C:\Users\Acer\Envs\adnwebsite-react\lib\site-packages\django\db\backends\utils.py", line 83, in _execute
    return self.cursor.execute(sql)
django.db.utils.ProgrammingError: column "none" does not exist
LINE 4: ...on','2002-03-18T06:18:45.284Z','Male','','Single',None,'B+',...

Я использую:

  1. Django 2.0.6
  2. Django Rest Framework 3.10.3
  3. PostgreSQL 11.3 (в моей разработке машина) и PostgreSQL 12.1 (на рабочем сервере) (хотя вышеуказанная ошибка еще не была реплицирована на сервер)

Примечание: raw_sql_select, raw_sql_insert , raw_sql_commit, raw_sql_rollback - это просто пользовательские вспомогательные функции, которые обрабатывают фактическое выполнение курсора в фоновом режиме.

1 Ответ

0 голосов
/ 18 марта 2020

Используйте SQL Параметры вместо % для создания сценария вставки.

Например -

# **WRONG** 
>>> cur.execute("INSERT INTO numbers VALUES (%s, %s)" % (10, 20)) 

# **correct**
>>> cur.execute("INSERT INTO numbers VALUES (%s, %s)", (10, 20))

Вы должны использовать cusrsor.execute в ваших вспомогательных функциях. Передайте значения в виде списка в качестве второго параметра в cusrsor.execute, как показано выше.

Сверните эту ссылку , чтобы узнать больше.

...