# React x Django
* [[React x Django] React Setup](/M7PPnLw4Q5yAFzGw3XoNsQ)
* [[React x Django] React Router](/96BTQu6zR_yUPpsR5P6tGA)
* [[React x Django] 後端環境架設](/ozxW1lbISAqnhUVzr-yNew)
* [[React x Django] Django REST Framework](/o6pWjIXQRHS1GHScFb2Z_A)
* [[React x Django] Fetching Data (Axios)](/MVJSODOHRK2FMaE4KRQ8FA)
* [[Django] 上傳圖片](/2hy5qHDJQmeFqAuVkSt95w)
* [[React x Django] Serializers 序列化](/isUMEhIISvudUKh0vo_rTA)
* [[React x Django] Simple JWT](/6pYUYhOBRGmUHZ0J76bFwA)
---
後端API製作
>[[React x Django] Update Profile Endpoint](/Et_SDYq1QRKLqUePqHN22w)
---
* [[React x Django] 前後端整合(上)- User Login Reducer and Action](/7uQrGG7zQ3i7knAcstJoCQ)
* [[React x Django] 前後端整合(下)- User Login Screen and Functionality](/dPqXR4LaTOK9S3u6_4TVmw)
* [[React x Django] 畫面整合(純顯示)](/sOdjbtedSWO2w1X6_KGL8A)
* [[React x Django] 顯示登入資訊( Logout & Login Navbar)](/9FjocdUbQj6Ys9z3uPov9A)
* [[React x Django] User Register](/Sa8VdhnjR2K4HVyzAizKsg)
* [[React x Django] Google登入登出功能](/nom1EsZ6QXm-mOjGjA90vw)
* [[React x Django] Update Profile Endpoint](/Et_SDYq1QRKLqUePqHN22w)
* [[React x Django] 有外來鍵的物件/外來鍵下拉式選單](/GpPj14j3Ru6hmvja60mZ7A)
---
小 Tips
* [[React x Django] Headers Config](/E9ZFwOAjTLyiRAfQjK41jA)
* [[React x Django] UseEffect 渲染無窮迴圈](/uRWZV5zsQZ2Kvpf9-xjvFw)
* [[React x Django] Checkbox](/d5qcAWr-R9CBsc0A9RBw9w)
* [[React] React-bootstrap Datepicker](/wZW1a5OjTGWhQlpe_T1YCw)
---
參考連結:
[Django ORM Cookbook](https://books.agiliq.com/projects/django-orm-cookbook/en/latest/index.html)
---
## DB 遷移
`python manage.py makemigrations`
`python manage.py migrate`
## BUGFIX
### 網路傳輸問題
#### 405 POST method not allowed
` "POST /api/school/create HTTP/1.1" 405 41`
解決方式:
要POST的網址最後少一個 `/`
#### AssertionError: Expected a `Response`, `HttpResponse` or `HttpStreamingResponse` to be returned from the view, but received a `<class 'str'>`
解決方式:
要在Response 前加入 `return Response`
### 系統端問題
#### Django後端改admin密碼
解決方式:
`python manage.py changepassword ME`
### 資料庫問題
#### 如果 `models.py` migrate 後還是 `No such table` 或 `No detection` ,`No migrations to apply`可以改執行
`python manage.py migrate --run-syncdb`
#### 初始makemigrations記得加上`my_app`名稱才能把migration都紀錄在資料夾中
`python manage.py makemigrations <my_app>`
#### 萬一migrate之後有欄位要加入,出現`no such column 'fields'`
處理方式:
先將`fields`從`models.py`中刪掉
執行
`python manage.py makemigrations`
`python manage.py migrate`
如果有錯誤,多執行下面這行
`python manage.py migrate --fake`
然後繼續把`fields`再重新加回 `models.py`
執行
`python manage.py makemigrations`
`python manage.py migrate`
### Serializer
#### SerializerMethodField
外來鍵關聯的物件有很多欄位我們是用不到的,都傳給前端會有資料冗餘,就需要我們自己去定製序列化外來鍵物件的哪些欄位
#### DRF模型序列化器默認僅返回數據庫中已存在字段,如果想新增輸出字段,該如何操作?
例如:輸出用戶角色時,順便輸出當前角色總共有多少用戶.
`models.py`
```python=
class Role(models.Model):
"""角色表,一的一方"""
name = models.CharField(max_length=30, unique=True, verbose_name=‘角色名稱‘) # 媒體運營,廣告運營,活動運營,財務,技術,唯一,必填
desc = models.CharField(max_length=100, null=True, blank=True, verbose_name=‘角色描述‘) # 非必填
class Meta:
db_table = ‘tb_role‘
verbose_name = ‘角色‘
verbose_name_plural = verbose_name
def __str__(self):
"""控制對象輸出內容"""
return self.name
class User(BaseModel):
"""用戶表,多的一方"""
account = models.CharField(max_length=30, unique=True, verbose_name=‘登錄賬戶‘) # 必填,唯一
password = models.CharField(max_length=100, null=True, blank=True, default=‘888888‘,
verbose_name=‘登錄密碼‘) # 非必填,默認888888,長度100是為了以後加密擴展
username = models.CharField(max_length=30, null=True, blank=True, verbose_name=‘用戶名稱‘) # 非必填
role = models.ForeignKey(Role, on_delete=models.CASCADE, related_name=‘user‘, verbose_name=‘角色‘)
class Meta:
db_table = ‘tb_user‘
verbose_name = ‘用戶‘
verbose_name_plural = verbose_name
def __str__(self):
"""控制對象輸出內容"""
return self.account
```
`serializer.py`
```python=
class RoleModelSerializer(serializers.ModelSerializer):
"""角色模型序列化器"""
user_count = serializers.SerializerMethodField(label=‘用戶數量‘) # 新增數據庫不存在字段用戶數量
class Meta:
model = Role
fields = [‘id‘, ‘name‘, ‘desc‘, ‘user_count‘]
def get_user_count(self, obj):
"""
返回當前角色用戶數量
固定寫法,obj代表Role實例對象,模型類配置了反向引用user代表當前角色用戶
"""
number = obj.user.count()
return number
```
### 外鍵資料顯示名稱而不是id 在Serilizers調整
希望在table上不要顯示id,而是外鍵的實際名稱
![](https://i.imgur.com/pkWGL0D.png)
```python=
class MemberSerializer(serializers.ModelSerializer):
family = serializers.SerializerMethodField(read_only = True)
intro_by = serializers.SerializerMethodField(read_only = True)
class Meta:
model = Member
fields = '__all__'
def get_family(self, obj):
family_one = obj.family
serializer = MemberSerializer(family_one, many=False)
return serializer.data['name']
def get_intro_by(self, obj):
intro_by_one = obj.intro_by
serializer = MemberSerializer(intro_by_one, many=False)
return serializer.data['name']
```
第2行 family:為我們宣告Member其中一個欄位 `family`
第3行 intro_by:為我們宣告Member其中一個欄位 `intro_by`
在前面先行宣告代表我們後面要進行處理
第8行 get_family 及 第15行 get_intro_by
名字不能改變,程式執行時要拿family與intro_by時就會去get_family 及 get_intro_by
而models中的外鍵是對Member,這邊則拿family的id去執行Member的Serilizer找出該 id 的member,然後我們回傳的時候要Member裡name的欄位
經過這個函式處理,family這個欄位會由 id 轉成 family 的 name 顯示回去
get_intro_by也是同樣道理
### Django ORM Self-Join
```manager = models.ForeignKey('self', on_delete=models.CASCADE)```
### ForeignKey does not allow null values
```
The blank option is used in the form validation, and the null is used when writing to database.
So you might add null=True to that field.
EDIT: continue the comment
Considering the two steps when saving object:
Validator(controlled by blank)
Database limitation(controlled by null)
For default option, take IntegerField for example,
default=5, blank=True, null=False, pass (1) even if you didn't assign a value(having blank=True), pass (2) because it has a default value(5) and writes 5 instead of None to DB.
blank=True, null=False, which pass (1) but not (2), because it attempts to write None to DB.
Thus, if you want to make a field optional, use either default=SOMETHING, blank=True, null=False or blank=True, null=True.
```
所以,如果有Foreign Key的欄位要optional的話
改成 `blank=True, null=True`
```python=
family = models.ForeignKey('self', on_delete=models.SET_NULL, blank=True, null=True,related_name="member_family_User")
```
#### Model 日期自動更新
```python=
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
```
#### BUG: __str__ returned non-string (type Student)
```python=
def __str__(self):
return self.student_name
```
serilizers.py需作修改
```python=
def __str__(self):
return str(self.student_name)
```
### Table 內排序
`models.py`中,每個table末端補上
```python=
class Meta:
ordering = ('-created_at',)
```
* 記得加上 `,` ,不然會出現
`'ordering' must be a tuple or list (even if you want to order by only one field).` 錯誤
### 多Table Join
---
### React
#### 下拉式選單
##### Uncaught Error: input is a void element tag and must neither have `children` nor use `dangerouslySetInnerHTML`.
##### `Warning: Each child in an array or iterator should have a unique 'key' prop.`
在 JSX 的 { } 中用 inline 的寫法:
```javascript=
{numbers.map((numbers) =>
<li key={numbers.toString()}>{numbers}</li>
)}
```
執行上面的例子,你可能有發現在 console 中 React 給你一個警告訊息是:
Warning: Each child in an array or iterator should have a unique 'key' prop.
`字面上的意思是,你需要給陣列中的每一個 React 元素一個唯一的編號 (unique key),編號是字串型態,設定在 key 這個屬性上。`
因為 React 需要知道實際上哪個元素是哪一個,React 的 Virtual DOM Diff 演算法才能確定哪些元素是需要在 DOM 中被新增的、哪些是要更新的、哪些是要被刪除的。
`React 用 key 在陣列中判斷是不是同一個元素物件。`
改成:
```javascript=
const listItems = numbers.map((numbers) =>
<li key={numbers.toString()}>{numbers}</li>
);`
```
或 每個元素不好想出個編號,或陣列中的元素順序是固定不變的
```javascript=
const todoItems = todos.map((todo, index) =>
// 利用 map function 的第二個 index 參數
<li key={index}>
{todo.text}
</li>
);
```
在我遇到的例子
將`<Dropdown.item></Dropdown.item>`加上 `index`, 及`key={index}`
```javascript=
{schools.map((school,index) =>{
return <Dropdown.Item eventKey={[school._id,school.name] } key={index} >{school.name}</Dropdown.Item>
})}
```
#### React顯示true or false
`沒辦法直接以oneStud.is_end顯示結果`
需改成
```javascript=
<td>{oneStud.is_end == true ? "是":"否"}</td>
```
#### React 畫面重新整理
`history.push(redirect)`:只是跳回頁面
如果需要重新整理:
`window.location.href = redirect`:不用再跳回以後按重新整理
#### Objects are not valid as a React child. If you meant to render a collection of children, use an array instead
代表傳入的東西homes為Array,沒辦法直接在前端上跑出來
需要用迭代取出來
`homes.map(home => (...))`
#### TypeError: history.push is not a function
```javascript=
function SettingScreen(history)
```
要改成
```javascript=
function SettingScreen({history})
```
#### Uncaught TypeError: Cannot read property 'push' of undefined (React-Router-Dom)
成因:因為history的傳導過程斷掉了,可能是內崁頁面所以吃不到history
解決方式:
手動給一次
```javascript=
//載入 useHistory
import { useHistory } from "react-router-dom";
```
```javascript=
//把值塞給history
let history = useHistory();
```
可正常跳轉了
```javascript=
history.push('/asdfg')
```
>ref:
[StackOverflow| uncaught-typeerror-cannot-read-property-push-of-undefined-react-router-dom](https://stackoverflow.com/questions/44009618/uncaught-typeerror-cannot-read-property-push-of-undefined-react-router-dom)
### 編輯時找不到match時會跳掉需要再按一次
將
```javascript=
if (!incomeContribute.context || incomeContribute._id != Number(incomeContributeContextId)){
history.push(redirect)
}
}else{
setContext(incomeContribute.context)
}
```
redirect移除,只進行判斷,不做任何處理
```javascript=
if (!incomeContribute.context || incomeContribute._id != Number(incomeContributeContextId)){
//保持無動作
}
}else{
setContext(incomeContribute.context)
}
```
---
### Uncaught Range Error invalid time value
原因:
因為我們的值傳入時的格式不正確
修改方式:
因為原本的值來自moment轉出來的,
所以先將拿出來的string `semester.start_date` 強轉化成moment後
再轉成Date型態
```javascript=
var start_date_for=moment(semester.start_date).toDate();
setStart_date(start_date_for)
```
### Check Reducer is Null?
使用`{outcome} || []` 來判斷,不要使用`{outcome} === null`
```javascript=
const outcomeDetailReducer = useSelector(state=>state.outcomeDetail)
const {errorOutcomeDetail, loadingOutcomeDetail, outcome} = outcomeDetailReducer
...
return (
...
{loadingOutcomeDetail ? <Loader />
: errorOutcomeDetail ? <Message variant='danger'>{errorOutcomeDetail}</Message>
:{outcome} || []? <h1>無支出</h1>
:<div>
支出項目: {outcome.name}
支出金額: {outcome.outcome_money}
</div>
}
...
)
```
### React Inline Color
```javascript=
<h3 style={{color:"red"}}>無資助項目</h3>
```
### Django jwt token error. "Given token not valid for any token type, token_not_valid"
調整抓token的方式
${userInfo.token} -> ${userInfo.access}
```javascript=
const config = {
headers: {
'Content-type': 'application/json',
Authorization: `Bearer ${userInfo.access}`
}
}
```